FutureTask
的几个关联方法
- <T> Future<T> submit(Callable<T> task);
- <T> Future<T> submit(Runnable task, T result);
- <T> Future<T> submit(Runnable task);
- void execute(Runnable command);
方法2要特别说明一下 result的作用是为了间接获取task执行结果(result不能为原始类型?)result要传入task或作为共享变量在task执行run 期间获取了结果,才能作为参数传入,否则没有意义。
例如
public class Submit2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Data data = new Data();
Future<Data> future = executor.submit(new Task(data), data);
System.out.println(future.get().getName());
}
}
class Data {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Task implements Runnable {
Data data;
public Task(Data data) {
this.data = data;
}
public void run() {
System.out.println("This is ThreadPoolExetor#submit(Runnable task, T result) method.");
data.setName("kevin");
}
}
摘抄至submit方法实例:https://www.cnblogs.com/yulinfeng/p/7039979.html
方法4 command参数可以传入 FutureTask 通过调用FutureTask.get()来实现等待任务执行完成并获取执行结果
使用FutureTask等待任务执行完成并获取执行结果经常使用方法1,方法3不能获取执行结果。方法2 和方法4不常用。
FutureTask 怎么实现的等待任务完成??
分析比较常用的关于 FutureTask的方法 AbstractExecutorService-submit
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
//把task封装成RunnableFuture
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
//FutureTask依赖 内部类Sync
sync = new Sync(callable);
}
不管FutureTask怎么封装 最后运行的都是 run方法。
public void run() {
sync.innerRun();
}
再看一下get方法 都是 依赖Sync实现。
public V get() throws InterruptedException, ExecutionException {
return sync.innerGet();
}
Sync 继承了AQS 说明 FutureTask的同步功能是依赖AQS实现的
继续分析 Sync
innerGet实现比较简单 通过获取锁实现等待执行完成。
V innerGet() throws InterruptedException, ExecutionException {
acquireSharedInterruptibly(0);
if (getState() == CANCELLED)
throw new CancellationException();
if (exception != null)
throw new ExecutionException(exception);
return result;
}
既然是获取锁 肯定有释放锁的地方
回顾一下AQS 中的acquireSharedInterruptibly方法
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
由子类实现
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
FutureTask中的tryAcquireShared的实现
protected int tryAcquireShared(int ignore) {
//根据任务是否完成返回1或-1
return innerIsDone() ? 1 : -1;
}
boolean innerIsDone() {
//如果 执行状态为RAN 或 CANCELLED且 runner == null,认为任务完成
return ranOrCancelled(getState()) && runner == null;
}
private boolean ranOrCancelled(int state) {
return (state & (RAN | CANCELLED)) != 0;
}
结合下AQS 中的acquireSharedInterruptibly方法分析,得出结论: 任务执行完成获取锁任务没完成进入锁获取队列。
innerRun 执行了callable获取了返回结果,并释放了锁。
void innerRun() {
//标识状态变成运行中
if (!compareAndSetState(READY, RUNNING))
return;
//获取当前线程 ,注意这里 runner 后面判断是否完成执行用到
runner = Thread.currentThread();
//再次检查状态
if (getState() == RUNNING) { // recheck after setting thread
V result;
try {
//执行callable
result = callable.call();
} catch (Throwable ex) {
setException(ex);
return;
}
//关键方法
set(result);
} else {
releaseShared(0); // cancel
}
}
set FutureTask的result 除非 这个future已经被set 成RAN和被取消
protected void set(V v) {
sync.innerSet(v);
}
void innerSet(V v) {
for (;;) {//CAS可能不成功需要循环
int s = getState();
//RAN 表示已经执行完成。
if (s == RAN)
return;
if (s == CANCELLED) {
// aggressively release to set runner to null,
// in case we are racing with a cancel request
// that will try to interrupt runner
releaseShared(0);
return;
}
//把状态改为完成。
if (compareAndSetState(s, RAN)) {
//赋值result
result = v;
//这里执行锁的释放
releaseShared(0);
done();
return;
}
}
}
回顾一下AQS 中的releaseShared方法
public final boolean releaseShared(int arg) {
//黄色的部分由子类实现
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
FutureTask中innerRun的实现
protected boolean tryReleaseShared(int ignore) {
runner = null;
return true;
}
总结:FutureTask依赖AQS中共享锁的获取和释放,实现等待任务完成的功能。
CompletionService
CompletionService 可以按照线程执行完成的顺序获取线程对应的返回结果。
CompletionService 目前只有一个实现类ExecutorCompletionService
(Completion:肯母扑勒深 ,完成)
如果想获得线程的执行结果,可以通过 线程池ThreadPoolExecutor.submit() 返回的FutureTask 的get方法等待任务完成后获取,如果是多个线程就要循环调用多个get()方法,而get方法是阻塞的,顺序调用耗时较长, 如果其中一个线程耗时较长,会影响其他 get方法。
ExecutorCompletionService依赖ExecutorService(通常是ThreadPoolExecutor),封装了FutureTask,在线程任务执行完成后将结果添加到 LinkedBlockingQueue中。ExecutorCompletionService的take()方法获取先执行完成的返回结果,调用LinkedBlockingQueue的take方法
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
executor.execute(new QueueingFuture(f));
return f;
}
public Future<V> submit(Runnable task, V result) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task, result);
executor.execute(new QueueingFuture(f));
return f;
}
上面代码中 与ThreadPoolExecutor相似,只有棕色部分不同,对FutureTask进行二次封装
下面是封装代码,done放在FutureTask分析中讲到是在线程任务完成后执行的。
private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
protected void done() { completionQueue.add(task); }
private final Future<V> task;
}
take
public Future<V> take() throws InterruptedException {
return completionQueue.take();
}
take实现比较简单,就是 依赖LinkedBlockingQueue的take方法。
获取返回结果 completionService.take().get() 不会阻塞,返回结果是线程任务执行完后放入的。