多线程 | 并发包中的Future、Callable、FutureTask 源码分析

引言

    @RequestMapping(value = "/socialInsuPayStatus",method =RequestMethod.PUT)
    public Callable<?> compSocialPay(@RequestBody ParamDto paramDto ){
        return ()-> bizService.compSocialInsuPay(paramDto );
    }

这是之前项目要求的接口风格,返回了一个Callable对象,大家可以先思考一下这么写有什么优点?

正文

1)首先看一下这几个类的结构关系:

这里写图片描述
这里写图片描述
主要作用:

Ruuable表示可被Thread执行的任务,它的run方法没有返回值,解决java 单继承的问题,将该实现传入thread类来启动。
Callable表示可以调用的任务,它的call方法可以有返回值,可以抛出显式的异常
Future:线程调度器,是对异步执行的任务的控制,包括取消任务,判断任务状态,获取任务结果等操作
RunnableFuture是对Runnable接口和Future接口的适配,表示可以被控制状态的Runnable
RunnableAdapter是对Runnable和Callalbe的适配,实现了Callable接口,并聚合了Runnable对象
FutureTask实现了RunnableFuture接口,通过RunnableAdapter对传入的Callable和Runnable参数进行统一处理。

来看一下这几个类的完整执行流程:

    //初始化线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        List<Future<Integer>> futures = new ArrayList<>();
        for (int j = 1; j <= 10; j++) {

            final int index = j;
            Future<Integer> future = threadPool.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    //第三个线程睡眠等待
                    System.out.println("---------");
                    if (index == 3) {
                        Thread.sleep(3000l);
                    }
                    return index;
                }
            });
            futures.add(future);
        }
        threadPool.shutdown();
        for (Future<Integer> future : futures) {
            System.out.println("线程:"+future.get()+" 任务执行结束:");
        }

1)首先看一下 threadPool.submit()方法,在 AbstractExecutorService中:

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        //实际返回的对象是 FutureTask
        return new FutureTask<T>(runnable, value);
    }

可以清晰看到这里实际上是将 callable实现 包装成 RunnableFuture,而RunnableFuture继承runnable接口,所以又作为参数在execute方法中执行,这个是线程池 ThreadPoolExecutor 中的方法。
其实这里也是线程池 execute 和 submit方法的区别与联系,顺便总结一下:

1)execute()方法只能进行任务的提交而不能获取该任务执行的结果, 但 submit()方法则既能进行任务的提交, 又能获取该任务执行的结果。
2)submit()方法就是对 execute()方法的一种封装, 它内部也是调用 execute()方法来实现任务提交的, 只是因为 submit()方法的返回值是一个 Future 对象。

2)future对象被包装成 worker对象,调用 addWorker() 方法,将线程加入到工作线程集合 workers中。

 public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
        //这里开始判断 线程数量和线程池中数量的大小,也就是线程池的几种状态
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

3)在 addWorker() 方法中,启动新线程来执行 worker 任务(futureTask对象此时被包装在 worker对象中)。

/** Delegates main run loop to outer runWorker  */
        public void run() {
           //调用runWorker方法,执行当前对象包含的 futureTask任务对象
            runWorker(this);
        }

4)开始执行 futureTask 对象中的 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对象
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                //如果执行结束,将执行结果设置到当前对象中的 outcome 变量上。
                    set(result);
            }
        } finally {
            runner = null;
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

5)futureTask 获取执行结果。

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);
    }

整个流程到这里结束,实际上就是将 futureTask 来包装执行线程和执行结果,执行结束,用该对象来判断线程执行状态来获取结果。

猜你喜欢

转载自blog.csdn.net/woshilijiuyi/article/details/79132315