线程的执行单元Runnable没有返回值怎么办
众所周知大名鼎鼎的Runnable是没有返回值的,为了解决这一问题Doug Lea大佬再juc包下面写了一个Callable类
public interface Callable<V> {
V call() throws Exception;
}
虽然但是,有这玩意也没有用啊是不是,Java创建线程的方式只有Thread,Thread只认Runnable...你这玩意给Thread也没用是不是。
聪明的Doug lea又写了一个类FutureTask,把Callable作为其属性
public class FutureTask<V> implements RunnableFuture<V> {
它继承了RunnableFuture,点进去看
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
发现RunnableFuture继承了Runnable,这就厉害了,这就意味着FutureTask也能被Thread当成可执行的单元了
ok解决了这个问题
FutureTask又是如何获取返回值的呢
先看它的get方法
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
// 点进去
return report(s);
}
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);
}
发现它当state为NORMAL的时候才会返回outcome,原来返回值在outcome里面啊。
outcome这个值是哪里来的,来看它的run方法
线程start了以后,被执行的会是run方法
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 执行Callable的call方法
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 设置outcome
outcome = v;
// 设置state为NORMAL
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
okk,在这里我们可以看到就是在run方法里面,执行了call方法,然后把返回值设置到outcome里面,再把state设置成NORMAL。是不是就很简单。
get的时候值没准备好怎么办
call这个方法可能执行很长时间,这个时候去get是没有值的
public V get() throws InterruptedException, ExecutionException {
int s = state;
// 不是结束状态
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
我们发现如果state如果小于等于COMPLETING,就是执行awaitDone
先看一下state,发现大于COMPLETING,都是已经执行完的状态
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; //
ok点进awaitDone方法
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
// 包装成WaitNode加入队列
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
// 有超时时间就挂起那么长时间
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
// 挂起当前线程
LockSupport.park(this);
}
}
发现在这里把这个线程封装进了WaitNode的队列,并且挂起了当前线程。
怎么唤醒呢?回到set方法
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
发现有个finishCompletion,点进去
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) { // 依次唤醒队列中的线程
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
好的,所以是在set完值之后如果有等待队列,就是唤醒他们,然后那些线程就可以开心的拿返回值了。
执行call的时候发生了异常怎么办
回到run方法
public void run() {
// 执行call
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
}
ok在catch里面setException,看看这个方法
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
发现就把异常放进outcome里面,状态设置成EXCEPTIONAL。和正常设置返回值一样,如果有等待队列,也回去唤醒队列中的线程。
等太长时间了我想取消这个任务了
okk有时候任务也会被取消,让我们看看
public boolean cancel(boolean mayInterruptIfRunning) {
// 就是设置一下state
// 看一下mayInterruptIfRunning,
// 如果在运行就改成INTERRUPTING
// 否则 CANCELLED
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
// 前面是NEW,这里也可能会变了状态
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
// 返回取消失败
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
// 改成INTERRUPTED
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
// 唤醒等待队列
finishCompletion();
}
return true;
}
ok看到上面我们知道cancel方法就是把state设置成INTERRUPTED或者是CANCELLED状态,如果设置状态时候值发生已经不是NEW就返回取消失败。如果有等待结果的队列,也会去唤醒一下。