FutureTask解析

FutureTask除了实现Future接口外,还实现了Runnable接口。因此其除了能交给Executor执行,也可以由调用线程直接执行(FutureTask.run())。以下是个人的经验总结,也参考了网上不少的文章,参考详见文末列表。

实现原理总结

关于实现原理,建议先看深入学习FutureTask文中的Why章节。

FutureTask类内部维护一个volatile int 类型的字段state,表示任务的执行状态。

state状态uml图

其中COMPLETING状态和INTERRUPTING状态是保持时间很短的中间状态NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTED是最终状态。

另外在FutureTask的run方法中有以下代码片段:

public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        //以下代码略
 }

UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))方法用于原子性设置变量对象字段值。this表示当前FutureTask实例,runnerOffset表示要修改的字段(runner)偏移量,null表示未修改时的期望值,Thread.currentThread()表示修改后的目标值。看以下代码片段:

    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;//表示执行Callable的线程
    private static final long runnerOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = FutureTask.class;
            runnerOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("runner"));
        }catch(Execption){
        }
    }

另外,参考文中也提到

发起任务线程跟执行任务线程通常情况下都不会是同一个线程,在任务执行线程执行任务的时候,任务发起线程可以查看任务执行状态、获取任务执行结果、取消任务等等操作。

这正符合了Executor框架中对任务的创建与分离的基本思想。这样做,既能充分利用多核CPU带来的执行工作效率上的提升,也能更好的解耦工作便于理解和维护。

常见使用方式

参考1中已经提到常见的三种使用方式,这里主要指出一些特别之处。

        /**
         * 第一种方式:Future + ExecutorService
         * Task task = new Task();
         * ExecutorService service = Executors.newCachedThreadPool();
         * Future<Integer> future = service.submit(task1);
         * service.shutdown();
         */


        /**
         * 第二种方式: FutureTask + ExecutorService
         * ExecutorService executor = Executors.newCachedThreadPool();
         * Task task = new Task();
         * FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
         * executor.submit(futureTask);
         * executor.shutdown();

第一种方式得到的异步结果对象future与第二种futureTask在本质上都是FutureTask类型,截止到JDK1.8

    <T> Future<T> submit(Callable<T> task);
    Future<?> submit(Runnable task);
    <T> Future<T> submit(Runnable task, T result);

这些方法的返回对象类型都是FutureTask

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容