Java中启动线程的3中方法,本文也是主要围绕着启动一个线程执行任务的方法为中心,比较详细的描述了源码中的实现。其实主要也是FutureTask这个实现类中的一些代码实现。如有不当之处,敬请指正。
本文起源还得从三行代码说起:
FutureTask future = new FutureTask(()->{return 111;}); //callable任务在FutureTask中是如何执行
new Thread(future).start();
//阻塞获取线程执行结果
System.out.println(future.get()); //为什么能够获取线程的执行结果
首先,线程执行的三种方式有一下三种,本文也是主要围绕第三种方式讲解其中的源码实现。
- 继承 Thread类重写run方法
- 实现Runnable接口并且实现run方法
- 实现Callable接口,通过FutureTask包装器来创建Thread线程。并且实现call方法,其中泛型是线程的返回值类型
小知识:@FunctionalInterface
这个方法是JDK1.8
函数式编程新特性加入的,对于只有一个抽象方法的抽象类,可以加入这个注解。在使用时可以直接使用函数式编程的方式直接使用。如:
FutureTask future = new FutureTask(()->{return 111;});
Future接口介绍:
/**
* 如果任务已经完成、已经取消或由于其他原因无法取消,则此尝试将失败。
* 如果成功,并且在调用{@code cancel}时该任务还没有启动,则该任务永远不会运行。
* 如果任务已经启动,则{@code mayInterruptIfRunning}参数确定执行该任务的线程是否应该在尝试停止任务时被中断。
*
* @param mayInterruptIfRunning 是否中断正在执行的任务
* @return 任务是否被正确中断(如果任务无法取消,通常是因为它已经正常完成;)
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* 任务在正常完成前是否被取消
*
* @return
*/
boolean isCancelled();
/**
* 返回任务是否完成
*
* @return
*/
boolean isDone();
/**
* 获取执行结果,阻塞等待完成后获取
*
* @return
* @throws InterruptedException 中断异常
* @throws ExecutionException 执行异常
*/
V get() throws InterruptedException, ExecutionException;
/**
* 设置超时时间,阻塞等待获取执行结果
*
* @param timeout 超时时间
* @param unit 时间单位
* @return
* @throws InterruptedException 中断异常
* @throws ExecutionException 执行异常
* @throws TimeoutException 超时异常
*/
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
接下来,介绍一下FutureTask这个实现类:
首先FutureTask中几个具体的变量如下:
/**
* 这个为四种状态的变化关系
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state;
private static final int NEW = 0; //新建
private static final int COMPLETING = 1; //执行完成中间状态
private static final int NORMAL = 2; //正常完成的
private static final int EXCEPTIONAL = 3; //异常完成的
private static final int CANCELLED = 4; //取消
private static final int INTERRUPTING = 5; //被打断(中间状态)
private static final int INTERRUPTED = 6; //中断
/** 对应的callable方法; 运行后会被设置为null */
private Callable<V> callable;
/** 线程执行的结果(或者返回的异常) */
private Object outcome;
/** 运行任务的线程 */
private volatile Thread runner;
/** 等待结果的等待节点队列(单链表结构,每个等待节点都有指向下一个等待节点的指针,以及等待结果的线程) */
private volatile WaitNode waiters;
首先,看下FutureTask中的run和cancel方法
public void run() { //执行Callable的call方法
if (state != NEW || //如果任务状态不为NEW或者CAS设置当前线程为执行线程失败,直接返回
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result; //放回的结果集合
boolean ran; //是否成功执行的标识
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran) //通过CAS设置任务状态为COMPLETING(执行中),并且将返回值设置在outcome属性上之后,改为NORMAL(正常完成)
set(result); //完成任务状态改变之后,会调用finishCompletion()方法,对等待队列上的线程unpark
}
} finally {
//将执行的线程引用释放,方便GC
runner = null; //为了防止对run()的并发调用,在状态确定之前,runner必须为非空。
int s = state; //在置空运行程序后必须重新读取状态,以防止中断泄漏,处理执行完之后线程被执行cancel
//如果线程是被打断(调用了cancel),执行被打断的逻辑,让出cpu,等待cancel方法将INTERRUPTING 修改为INTERRUPTED,退出执行
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
/**
* 确保来自可能的cancel(true)的任何中断只在运行或runAndReset时交付给任务。
*/
private void handlePossibleCancellationInterrupt(int s) {
if (s == INTERRUPTING)
//死循环,响应中断,知道cancel方法将state设置成INTERRUPTED后退出
while (state == INTERRUPTING)
Thread.yield(); //如果线程执行被打断,循环并且让出CPU
}
/**
* 如果任务已经完成、已经取消或由于其他原因无法取消,则此尝试将失败。
* 如果成功,并且在调用{@code cancel}时该任务还没有启动,则该任务永远不会运行。
* 如果任务已经启动,则{@code mayInterruptIfRunning}参数确定执行该任务的线程是否应该在尝试停止任务时被中断。
*
* @param mayInterruptIfRunning 是否中断正在执行的任务
* @return 任务是否被正确中断(如果任务无法取消,通常是因为它已经正常完成;)
*/
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW && //如果不中断执行中的线程,直接将状态改为CANCELLED
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) { //如果中断执行中的线程,调用线程的interrupt方法,并且将任务状态修改为INTERRUPTED
try {
Thread t = runner;
if (t != null)
t.interrupt(); //设置线程中断标记
} finally { // 设置中断INTERRUPTING状态为INTERRUPTED
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion(); //unPark阻塞获取结果的set方法,删除并通知所有等待的线程,调用done(),并使callable为空。
}
return true;
}
接下来,讲解一下finishCompletion()
这个方法,这个是unpark阻塞等待结果的线程的关键所在
/**
* 删除并通知所有等待的线程,调用done(),并使callable为空。
*/
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) { //循环执行每一个waiters中的WaitNode节点,WaitNode为单链表结构
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {//CAS删除waiters的头节点
for (;;) {
Thread t = q.thread; //unPark WaitNode中的线程,处于park状态的线程被唤醒,可以去抢占CPU资源去获取任务执行结果
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next; //循环唤醒下一个节点
if (next == null) //waiters中所有节点被唤醒就退出
break;
q.next = null; // 断开当前node的链,帮助GC回收当前节点
q = next;
}
break;
}
}
done(); //钩子函数,可以自己实现一些线程执行成功之后需要执行的代码
callable = null; // 将Callable任务设置为null
}
获取FutureTask的执行结果:get()和get(long timeout, TimeUnit unit)
后者是设置超时时间去获取
/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING) //如果任务还在正常执行,未完成,并且没有抛出异常,则等待执行完成
s = awaitDone(false, 0L);
return report(s); //线程执行结束后,调用unPark方法,进入返回,获取结果
}
/**
* @throws CancellationException {@inheritDoc}
*/
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
if (s <= COMPLETING && //如果任务还在正常执行,未完成,并且没有抛出异常,则等待执行完成
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)//如果等待时间到了之后,线程还在正常执行,则抛出超时异常
throw new TimeoutException();
return report(s);
}
其中get()和get(long timeout, TimeUnit unit)
最终都进入awaitDone(boolean timed, long nanos)
方法阻塞等待结果,区别是如果没有超时时间则nanos为0L。
/**
* 等待执行完成,或者在中断和超时时候退出
* @param timed 是否允许超时
* @param nanos 超时时间
* @return 任务执行的状态
*/
private int awaitDone(boolean timed, long nanos)
throws InterruptedException { //一般会被打断的方法都会抛出InterruptedException中断异常
final long deadline = timed ? System.nanoTime() + nanos : 0L; //如果允许超时,设置,否则为OL
WaitNode q = null;
boolean queued = false;
for (;;) {
if (Thread.interrupted()) { //如果当前线程被中断,抛出异常,并且将等待节点移除出队列
removeWaiter(q);
throw new InterruptedException();
}
int s = state; //任务的执行状态
if (s > COMPLETING) { //如果任务超出COMPLETING(也就是说执行完成,异常或者中断...),将等待
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) //如果任务正在执行,当前线程让出CPU给其他线程执行
Thread.yield();
else if (q == null)
q = new WaitNode(); //等待节点为null,新建一个waitNode,WaitNode为单链表结构,结构如下块代码
else if (!queued) //CAS设置当前等待节点的next指针为waiters,并且把当前新建的waitNode节点设置为waiters
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) { //如果设置了超时时间
nanos = deadline - System.nanoTime();
if (nanos <= 0L) { //超时直接返回状态
removeWaiter(q);// 从waiters中删除node节点,并且将node.thread设置为null加速线程资源的回收
return state;
}
LockSupport.parkNanos(this, nanos);//阻塞一定时间,等待执行结果,会被中断打断
}
else
LockSupport.park(this);//阻塞,等待执行结果,会被中断打断
}
}
/**
* 记录等待线程的节点,为单链表结构,其中的线程就是调用get方法的线程
*/
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
最后调用report方法获取执行结果
/**
* Returns result or throws exception for completed task.
* 为已完成的任务返回结果或抛出异常。
* @param s completed state value
*/
@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x; //正常返回
if (s >= CANCELLED)
throw new CancellationException(); //被取消返回取消异常
throw new ExecutionException((Throwable)x); //返回其他执行异常
}
上述代码,基本将FutureTask执行(取消)一个任务以及获取执行结果(异常)能够串起来了。接下来画图总结一下,也能够帮助更好的梳理其中的关系。
再次声明一下任务的属性值和几种状态以及各个状态之间的变化关系:
/**
* 这个为四种状态的变化关系
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state;
private static final int NEW = 0; //新建
private static final int COMPLETING = 1; //执行完成中间状态
private static final int NORMAL = 2; //正常完成的
private static final int EXCEPTIONAL = 3; //异常完成的
private static final int CANCELLED = 4; //取消
private static final int INTERRUPTING = 5; //被打断(中间状态)
private static final int INTERRUPTED = 6; //中断
/** 对应的callable方法; 运行后会被设置为null */
private Callable<V> callable;
/** 线程执行的结果(或者返回的异常) */
private Object outcome;
/** 运行任务的线程 */
private volatile Thread runner;
/** 等待结果的等待节点队列(单链表结构,每个等待节点都有指向下一个等待节点的指针,以及等待结果的线程) */
private volatile WaitNode waiters;