Thread
类和Runnalble
接口来让我们实现自己的“线程”类。Thread
类,并重写run
方法;Runnable
接口的run
方法;Thread
和Runnable
来写一个Java多线程程序。Thread
类:start()
方法后,该线程才算启动!我们在程序里面调用了start()方法后,虚拟机会先为我们创建一个线程,然后等到这个线程第一次得到时间片时再调用run()方法。注意不可多次调用start()方法。在第一次调用start()方法后,再次调用start()方法会抛出异常。
Runnable
接口(JDK 1.8 +):Runnable
是一个函数式接口,这意味着我们可以使用Java 8的函数式编程来简化代码。Thread
类是一个Runnable
接口的实现类,我们来看看Thread
类的源码。Thread
类的构造方法,发现其实是简单调用一个私有的init
方法来实现初始化。init
的方法签名:init
方法的这些参数:inheritedAccessControlContext
。这个变量有点神奇。它是一个私有变量,但是在Thread
类里只有init
方法对它进行初始化,在exit
方法把它设为null
。其它没有任何地方使用它。一般我们是不会使用它的,那什么时候会使用到这个变量呢?可以参考这个stackoverflow的问题:Restrict permissions to threads which execute third party software;
ThreadLocal
,见片段4,Thread
类里面有两个私有属性来支持ThreadLocal
,我们会在后面的章节介绍ThreadLocal
的概念。Thread
类或者实现Runnable
接口这两种方式,它们之间有什么优劣呢?Runnable
接口”这种方式来自定义线程类。Runnable
和Thread
来创建一个新的线程。但是它们有一个弊端,就是run
方法是没有返回值的。而有时候我们希望开启一个线程去执行一个任务,并且这个任务执行完成后有一个返回值。Callable
接口与Future
类为我们解决这个问题,这也是所谓的“异步”模型。Callable
与Runnable
类似,同样是只有一个抽象方法的函数式接口。不同的是,Callable
提供的方法是有返回值的,而且支持泛型。Callable
的呢?Callable
一般是配合线程池工具ExecutorService
来使用的。我们会在后续章节解释线程池的使用。这里只介绍ExecutorService
可以使用submit
方法来让一个Callable
接口执行。它会返回一个Future
,我们后续的程序可以通过这个Future
的get
方法得到结果。Future
接口只有几个比较简单的方法:cancel
方法是试图取消一个线程的执行。boolean
类型的返回值是“是否取消成功”的意思。参数paramBoolean
表示是否采用中断的方式取消线程执行。Callable
来代替Runnable
。如果为了可取消性而使用 Future
但又不提供可用的结果,则可以声明 Future<?>
形式类型、并返回 null
作为底层任务的结果。Future
接口。这个接口有一个实现类叫FutureTask
。FutureTask
是实现的RunnableFuture
接口的,而RunnableFuture
接口同时继承了Runnable
接口和Future
接口:FutureTask
类有什么用?为什么要有一个FutureTask
类?前面说到了Future
只是一个接口,而它里面的cancel
,get
,isDone
等方法要自己实现起来都是非常复杂的。所以JDK提供了一个FutureTask
类来供我们使用。submit
方法是没有返回值的。这里实际上是调用的submit(Runnable task)
方法,而上面的Demo,调用的是submit(Callable<T> task)
方法。FutureTask
直接取get
取值,而上面的Demo是通过submit
方法返回的Future
去取值。state表示任务的运行状态,初始状态为NEW。运行状态只会在set、setException、cancel方法中终止。COMPLETING、INTERRUPTING是任务完成后的瞬时状态。