Java高并发高性能编程(多线程,协程,Actor,RxJava、Akka、Reactor)

Java多线程基础知识和Doug Lea 李大爷

2020-05-24  本文已影响0人  仕明同学

基础知识

1、CPU核心数和线程数的关系

image.png
2、CPU时间片轮转机制

多线程程序需要注意事项

Java里的程序天生就是多线程的

image.png

一个Java程序从main()方法开始执行,然后按照既定的代码逻辑执行,看似没有其他线程参与,但实际上Java程序天生就是多线程程序,因为执行main()方法的是一个名称为main的线程。

线程的启动与中止

启动

启动线程的方式有:

第1、2方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
但是其实只有两种,因为第三种的方式看着像是实现了Callable的接口,但是呢,其实到底还是实现了Runnable的接口,一个类实现了Callable,然后new 出对象,对象交给FutureTask,然后通过线程start

image.png
1、看FutureTask这个类,是实现了RunnableFuture接口
image.png
2、RunnableFuture继承了,Runnable的接口和Future接口,所以到底还是Runnable的之类,所以就只有两种
image.png
Callable、Future和FutureTask

Runnable是一个接口,在它里面只声明了一个run()方法,由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。


image.png

Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call(),这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。

Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。


image.png

因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。


image.png

FutureTask类实现了RunnableFuture接口,RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

事实上,FutureTask是Future接口的一个唯一实现类。

要new一个FutureTask的实例,有两种方法

中止

线程自然终止:要么是run执行完成了,要么是抛出了一个未处理的异常导致线程提前结束。

手动中止

暂停、恢复和停止操作对应在线程Thread的API就是suspend()、resume()和stop()。但是这些API是过期的,也就是不建议使用的。不建议使用的原因主要有:以suspend()方法为例,在调用后,线程不会释放已经占有的资源(比如锁),而是占有着资源进入睡眠状态,这样容易引发死锁问题。同样,stop()方法在终结一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序可能工作在不确定状态下。正因为suspend()、resume()和stop()方法带来的副作用,这些方法才被标注为不建议使用的过期方法。

安全的中止则是其他线程通过调用某个线程A的interrupt()方法对其进行中断操作, 中断好比其他线程对该线程打了个招呼,“A,你要中断了”,不代表线程A会立即停止自己的工作,同样的A线程完全可以不理会这种中断请求。因为java里的线程是协作式的,不是抢占式的。线程通过检查自身的中断标志位是否被置为true来进行响应,线程通过方法isInterrupted()来进行判断是否被中断,也可以调用静态方法Thread.interrupted()来进行判断当前线程是否被中断,不过Thread.interrupted()会同时将中断标识位改写为false。

如果一个线程处于了阻塞状态(如线程调用了thread.sleep、thread.join、thread.wait、),则在线程在检查中断标示时如果发现中断标示为true,则会在这些阻塞方法调用处抛出InterruptedException异常,并且在抛出异常后会立即将线程的中断标示位清除,即重新设置为false。

不建议自定义一个取消标志位来中止线程的运行。因为run方法里有阻塞调用时会无法很快检测到取消标志,线程必须从阻塞调用返回后,才会检查这个取消标志。这种情况下,使用中断会更好,因为,一、一般的阻塞方法,如sleep等本身就支持中断的检查,二、检查中断位的状态和检查取消标志位没什么区别,用中断位的状态还可以避免声明取消标志位,减少资源的消耗。

注意****:处于****死锁状态****的****线程无法被中断

几个方法

一张图

image.png

一个线程新建,到start()-就绪了,如果获取join()执行权就会运行,时间片到期 yield就会从运行状态转化到就绪
运行的状态,可以通过sleep() 到阻塞状态,同时run()到结束,也是就是一个死亡的状态。Stop不建议去使用,因为这样子会导致线程有很多的碎片化的空间,线程中的任务没有去执行完成,也可以通过wait 去到阻塞的情况
线程阻塞可以通过 notify notifyAll去唤醒他,同时sleep时间到也可以到就绪的状态,如果一个线程处于了阻塞状态(如线程调用了thread.sleep、thread.join、thread.wait、),则在线程在检查中断标示时如果发现中断标示为true,则会在这些阻塞方法调用处抛出InterruptedException异常,并且在抛出异常后会立即将线程的中

关于 yield的方法,在 ConcurrentHashMap 类中的initTable方法中就调用了

image.png image.png

关于Doug Lea ,李大爷,这句话可以这么说:编程不识DougLea,写尽java也枉然

image.png

道格拉斯·李(Douglas S. Lea)是纽约州立大学奥斯威戈分校计算机科学教授和现任计算机科学系主任,他专门研究并发编程并发数据结构的设计。他是Java Community Process执行委员会的主席,并担任JSR 166主席,该委员会将并发实用程序添加到Java编程语言中(请参阅Java concurrency)。2010年10月22日,Doug Lea通知Java社区流程执行委员会,他不赞成连任。 Lea再次当选为2012 OpenJDK理事会的一般会员

上一篇下一篇

猜你喜欢

热点阅读