「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。
1、Runnable与Callable
JDK1.0版本: Runnable
public class RunnableDemo {
public static void main(String[] args) {
Runnable task = () -> {
System.out.println("任务执行了");
};
new Thread(task).start();
}
}
Runnable接口的实例对象用来表示工作内容,将它传递给线程,让线程完成这项工作。Runable对象可以作为方法参数传递,可以被放入到队列中,可以跨越网络传递,也可以被保存至文件中。然后,这样的Runnable对象不论被传递到哪台计算机中的哪个线程中,都可以运行。这时,可以将Runbable接口看作GoF的Command模式。
JDK1.5版本:Future和Callable
public class FutureTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<Integer> callable = () -> {
// 代表耗时任务
Thread.sleep(60000);
System.out.println("输出结果:1");
return 1;
};
FutureTask<Integer> task = new FutureTask<>(callable);
new Thread(task).start();
while (!task.isDone()) {
try {
task.get(2, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.out.println("任务尚未完成");
}
}
System.out.println("任务已完成");
}
Future在英语中有“期货”的意思,买卖双方签订合约,约定在将来某一个时间进行货物的交割。 同理,假设有一个方法需要花费很长时间才能获取运行结果,那么与其一直等结果,不如先拿一张“提货单”。获取提货单并不耗费时间,这里的“提货单”就称为Future。 获取Future角色的线程会在稍后使用Future角色来获取运行结果。如果运行结果已经出来了,那么直接领取即可;如果运行结果还没有出来,那么需要等待结果出来。 java.util.concurrent.Callable接口将“有返回值的某种处理的调用”抽象化了。Callable接口声明了call方法。call方法与Runnable接口的run方法相似,不同的是call方法有返回值。
| 对比 | Runnable | Callable |
|---|---|---|
| 方法返回值 | 没有返回值 | 有返回值 |
| 能否抛出受检异常 | 不能 | 可以 |
| 在Thread类中使用 | 可以 | 不可以 |
| 在ExecutorService中使用 | 可以 | 可以 |
| 意义 | 表示线程的工作内容 | 表示线程的工作内容 |
2.Future实现
Future表示异步计算的结果,提供了一些方法来检查计算是否完成,等待其完成以及取回计算结果。计算完成后,只能使用get方法获得结果,必要时阻塞直到计算准备好为止。通过执行cancel方法来取消计算。提供了其他方法用来确定任务是正常完成还是被取消了。
FutureTask包装器是一种非常便利的机制,可将Callable转换成Future和Runnable,它同时实现二者的接口,代表了可取消的异步计算。
outcome字段代表计算结果
runner字段代表执行这个任务的线程
waiters则是一个类似AQS中等待队列的单向链表,保存因为等待计算结果而休眠的线程;
callable字段代表某种处理的调用
还有有一个state字段,代表这个FutureTask实例的生命周期中的某个状态:
FutureTask的生命周期,最初为NEW。运行状态仅在set、setException和cancel方法中转换为终态。在完成期间,状态可能会处于瞬时态COMPLETING(正在设置结果时)或者INTERRUPTING(中断执行者以满足cancel(true)时)。
下面沿着FutureTask的生命周期变化结合代码分析:
实例化一个FutureTask时,状态为NEW
// 将Callable实例包装为FutureTask
public FutureTask(Callable<> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
// 状态初始化为NEW
this.state = NEW;
}
线程启动,执行FutureTask的run方法:
// 将Callable实例包装为FutureTask
public void run() {
// 1.状态不为NEW或者runner字段不为NULL,任务已经被其他线程执行或取消,直接返回
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 {
// 2.1执行相关计算,并获取结果
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
// 3.任务执行异常,设置该异常为返回结果
setException(ex);
}
if (ran)
// 2.2设置结果
set(result);
}
} finally {
runner = null;
// 重新检查状态,避免泄露中断
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
// 2.2设置结果
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
// 设置终态为NORMAL
UNSAFE.putOrderedInt(this, stateOffset, NORMAL);
// 清理因为get而被阻塞的线程
finishCompletion();
}
}
// 3.设置异常为返回值
protected void setException(Throwable t) {
// 3.1设置状态为瞬时态COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 设置结果为所抛出异常
outcome = t;
// 设置终态为异常
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL);
// 清理因为get而被阻塞的线程
finishCompletion();
}
}
其他线程调用cancel方法取消任务
public boolean cancel(boolean mayInterruptIfRunning) {
// 计算已设置结果或者状态已被其他线程修改,则取消失败
if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
// 如果可以在运行中中断,则中断该线程,并设置状态为TERRUPTED
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
// 清理因为get而被阻塞的线程
finishCompletion();
}
return true;
}
最后是获取计算结果的get方法:
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
// 1.任务还没结果且等待指定时间后还没有结果,则抛出超时异常
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
// 2.成功获得结果
return report(s);
}
// 2.获得结果
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也有一个类似AQS中的等待队列,休眠的线程以单向链表的形式组织起来:
static final class WaitNode {
// 调用get方法的线程
volatile Thread thread;
// 后继节点
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
// 计算休眠截止时间
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
// 是否入队
boolean queued = false;
for (;;) {
// 如果线程被中断,从等待队列中移除,抛出中断异常
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
// 计算产生结果,返回状态
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
// 即将获得结果,不需要休眠,让出时间片
else if (s == COMPLETING)
Thread.yield();
// 还未初始化WaitNode节点
else if (q == null)
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);
}
}
// 任务完成、取消、异常后,移除并唤醒所有等待线程
private void finishCompletion() {
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;
}
}
// 自定义实现,可以在方法实现中查询status已确定这个task是否已经被取消
done();
callable = null;
}