A brief analysis of the asynchronous principle of Callable

Callable asynchronous execution should not be unfamiliar, so how is it used in java? How is it achieved? Below we step by step, slowly analyze. 
Let's first look at an example, implement the Callable interface, and perform asynchronous calculations:

package com.demo;
import java.util.concurrent.*;
public class Demo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        Future<String> future = executor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("call");
                TimeUnit.SECONDS.sleep(1);
                return "str";
            }
        });
        System.out.println(future.get());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

This code is a very simple way to use Callable to perform asynchronous operations, and the result can be executed by itself.

How to implement asynchrony

To calculate without blocking the current thread, another thread must be needed to execute the specific business logic. As you can see from the above code, the Callable is put into the thread pool, waiting for execution, and returns futrue immediately. It can be guessed that the result of Callable needs to be obtained from the Future, then the reference of the Future must be shared by two threads. After a thread is executed, it changes the status bit of the Future and wakes up the thread suspended on the get. Is this the case? ?

Source code analysis

First, we start with task submission. The source code in AbstractExecutorService is as follows:

    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) {
        return new FutureTask<T>(runnable, value);
    }
        public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

It can be seen that the Callable task is packaged into a RunnableFuture object. The task is submitted through the execute method of the thread pool and the object itself is returned immediately. The thread pool accepts Runnable, so RunnableFuture must inherit Runnable. Let's take a look at its inheritance structure. 
这里写图片描述 
It is clear from inheritance that FutureTask is a synthesis of Runnable and Future. 
We should have some clues here. The key point should be on the FutureTask object. The thread pool just provides a thread to run the run method in the FutureTask.

FutureTask

From the above analysis, the FutureTask is shared by the producer and the consumer. The producer runs the run method to calculate the result, and the consumer obtains the result through the get method, so it must be notified. How to notify, it must be the status bit change and wake up the thread. 
FutureTask state

    //FutureTask类中用来保存状态的变量,下面常量就是具体的状态表示
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

run method

//我修剪后的代码,可以看出其逻辑,执行Callable的call方法获取结果
 public void run() {   
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result = c.call();
                set(result);
            }
        }
        //把结果保存到属性字段中,finishCompletion是最后的操作,唤醒等待结果的线程
        protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL);//正常结束设置状态为NORMAL
            finishCompletion();
        }
    }
    //waiters是FutureTask类的等待线程包装类,以链表的形式连接多个,WaitNode对象是在调用get方法时生成,并挂起get的调用者线程
     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); //唤醒get上等待的线程
                        if (next == null)
                        break;
                    }
                   break;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

waiting thread

为了清除的看到如何挂起get的线程,我们可以分析下get的源码

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L); //会一直挂起知道处理业务的线程完成,唤醒等待线程
        return report(s);
    }
    public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        int s = state;
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)//通过判断返回的状态是否为已完成做不同的处理
            throw new TimeoutException();
        return report(s);
    }
private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
            int s = state;
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            else if (q == null)
                q = new WaitNode();
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            else if (timed) {  //如果是超时的get那么会挂起一段时间
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {//等待时间过后则会移除等待线程返回当前futureTask状态
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

如果想搞明白可以自行研究下,这种经过优化的并发代码确实可读性差,基本原理就是生产者与消费者模型。

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326692380&siteId=291194637