JUC源码分析-FutureTask,CompletionService

FutureTask

的几个关联方法

  1. <T> Future<T> submit(Callable<T> task);
  2. <T> Future<T> submit(Runnable task, T result);
  3. <T> Future<T> submit(Runnable task);
  4. 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() 不会阻塞,返回结果是线程任务执行完后放入的。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值