[TOC]
Java入门类和接口
Thread类和Runnable接口
Java中,JDK提供了Thread类和Runnable接口,我们有两种方法来实现自己的线程类。
- 继承
Thread类,并重写run方法。 - 实现
Runnable接口的run方法。
继承Thread类
1 | public class MyThread extends Thread { |
注意:一定要调用start()方法该线程才算启动。
new一个Thread,线程进入新建状态;调用start()方法会启动一个线程并使线程进入就绪状态,当分配到CPU时间片就可以运行了。start()方法会执行线程的相应准备工作,然后自动执行run()方法的内容,这才是真正的多线程。而直接调用run()方法,会把run()方法当成一个main线程下的普通方法去执行,还是在主线程中。
多次调用start()方法会抛出java.lang.IllegalThreadStateException异常。
实现Runnable()接口
Runnable()接口源码如下:
1 |
|
可以看到Runnable接口是一个函数式接口,这代表着可以使用函数式编程简化。
1 | public class ThreadTest { |
不采用lambda要这样用:
1 | public class ThreadTest { |
Thread类构造器
Thread类是Runnable接口的实现类,其构造方法源码如下:
1 | /** |
可以看到Thread类的构造器会调用私有的init()方法来实现初始化。init方法的方法签名如下:
1 | private void init(ThreadGroup g, Runnable target, String name, |
init方法参数的解释:
g:线程组,制定这个线程在哪个线程组下target:指定要执行的任务;name:线程的名字,多个线程的名字是可以重复的。如果不指定名字,会默认指定为"Thread-" + nextThreadNum()。acc:用于初始化私有变量inheritedAccessControlContext。inheritThreadLocals:可继承的ThreadLocal,Thread类中有两个私有属性来支持ThreadLocal。
大多数情况下我们调用下面两种构造方法:
Thread(Runnable target)Thread(Runnable target, String name)
Thread类的常用方法
currentThread():静态方法,返回对当前正在执行的线程对象的引用。start():开始执行线程的方法,Java虚拟机会调用线程内的run方法。yield():当前线程愿意让出对当前处理器的占用。要注意的是,就算当前线程调用了yield()方法,程序在调度的时候,也还有可能继续运行这个线程的。sleep():静态方法,使当前线程睡眠指定时间。join():使当前线程等待另一个线程执行完毕后再继续执行,内部调用的是Object类的wait()方法。
Thread类与Runnable接口的比较
实现一个自定义线程类,可以有继承Thread类和实现Runnable接口两种方法。
- 由于Java单继承、多实现的特点,
Runnable接口使用更加灵活; Thread类就是Runnable接口的实现;Runnable接口更加符合面对对象,将线程单独进行对象的封装;Runnable接口的出现,降低了线程对象与线程任务的耦合性;- 如果不需要使用
Thread类的其他方法,使用Runnable接口更加轻量
综上,我们通常优先使用Runnable接口来自定义线程类。
Callable、Future与FutureTask
通常我们使用Runnable和Thread来创建一个线程,但是run方法是没有返回值的。如果我们希望执行一个任务后返回一个值,我们可以使用JDK提供的Callable接口和Future类。这也是所谓的“异步”模型。
Callable接口
Callable接口与Runnable接口类似,同样是一个函数式接口,不同的是Callable接口有返回值且支持泛型。
1 |
|
Callable接口一般是配合线程池工具ExecutorSevice来使用的。ExecutorService可以使用submit方法来让一个Callable接口执行,它会返回一个Future,我们后续的程序可以通过这个Future的get方法得到结果。一个简单的demo如下:
1 | //自定义Callable |
Future接口
Future接口的源码如下:
1 | public interface Future<V> { |
cancel方法是试图取消一个线程的执行。注意是试图取消,不一定取消成功。因为任务可能已完成、已取消或者不可取消。最后返回是否取消成功。
参数mayInterruptIfRunning代表是否采用中断的方式取消线程执行。
所以有时候,为了让线程有能够取消的可能,就会使用Callable代替Runnable。若只是为了可取消性,而不需要结果,可以声明Future<?>形式类型,并返回null作为结果。
FutureTask类
Future接口有一个实现类FutureTask,这个类实现了RunnableFuture接口,而RunnableFuture接口同时继承了Runnable接口和Future接口。
RunnableFuture接口源码如下:
1 | public interface RunnableFuture<V> extends Runnable, Future<V> { |
Future只是一个接口,里面的cancel、get、isDone等方法自己实现会很复杂,所以JDk提供了一个FutureTask类。使用示例:
1 | public class ThreadTest implements Callable<Integer> { |
这里调用submit方法是没有返回值的。这里实际上调用的是submit(Runnable task)方法,而上面那个demo中,调用的是submit(Callable<T> task)方法。
这里是使用futureTask直接get取值,而上面Demo中是使用返回的Future去取值。
在高并发的情景下,有可能Callable和FutureTask会创建多次。FutureTask能确保在高并发下任务只被执行一次。
FutureTask的几个状态
1 | /** |
state表示任务的运行状态。初始状态为NEW。运行状态只会在set、setException、cancel方法中终止。COMPLETING、INTERRUPTING是任务完成后的瞬时状态。