java concurrent asynchronous programming interface original ten live now only need one interface to get!

什么?对你没有听错,也没有看错 ..多线程并发执行任务,取结果归集~~ 不再忧愁....感谢大家的双击+点赞和关注 下面起锅烧油


introduction

First look at some of the APP acquisition of data, and so on, a page for more than N, a user behavior data of up to about 10, such as: the number of thumbs up, the number of articles published, the number of points like this, the number of messages, the number concern, collectibles number, number of fans, card number coupon, red number ........... is really more ~ ​​we look at these figures:

Usually to get an interface to a data interface (10 queries because when you write together, it is estimated that up to half a minute to respond), a page on the N multi-interface, the front end of the baby is really exhausted, front opening multithreading Leia, we do want to look at the rear end of the front end of the body mass of babies, after all, there is a saying " programmer programmer - why make things difficult ."

Today we can interface a data thief TM fast return - also address the distress serial programming, programming obstruction caused ~

Multi-threaded tasks, take the results of imputation

Today pig is the Future, FutureTask, ExecutorService ...

  • Spend FutureTask task to obtain the results of all ages, there is the CPU consumption. FutureTask locking can also be done (Future implements semantic, represent an abstract calculated results). By Callable (the equivalent of a can generate results Runnable) as an attribute, and then put it himself as an actuator to inherit Runnable, a FutureTask, is actually a support asynchronous task execution is canceled behavior .
  • Callable is a callback interface, you can declare a generic return type, method and thread to Runnable is executed. This is very simple - we want to go in-depth understanding of the source code to see good because it is really very simple ~ ~
  • FutureTask realized the Future, provides start, cancel, query and other functions, and implements the Runnable interface, you can submit to the thread.
  • Java Concurrency tools three tricks 状态,队列,CAS

status

 /**
     * The run state of this task, initially NEW.  The run state
     * transitions to a terminal state only in methods set,
     * setException, and cancel.  During completion, state may take on
     * transient values of COMPLETING (while outcome is being set) or
     * INTERRUPTING (only while interrupting the runner to satisfy a
     * cancel(true)). Transitions from these intermediate to final
     * states use cheaper ordered/lazy writes because values are unique
     * and cannot be further modified.
     *
     * Possible state transitions:        //可能发生的状态过度过程
     * NEW -> COMPLETING -> NORMAL        // 创建-->完成-->正常
     * NEW -> COMPLETING -> EXCEPTIONAL   // 创建-->完成-->异常
     * NEW -> CANCELLED                   // 创建-->取消
     * NEW -> INTERRUPTING -> INTERRUPTED // 创建-->中断中-->中断结束
     */
    
    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;    // 中断结束      任务被中断

    /** The underlying callable; nulled out after running */
    private Callable<V> callable;
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;
复制代码

Figure do not understand it:

  • Future

      (1)cancle    可以停止任务的执行 但不一定成功 看返回值true or  false
      (2)get       阻塞获取callable的任务结果,即get阻塞住调用线程,直至计算完成返回结果
      (3)isCancle  是否取消成功
      (4)isDone    是否完成
    复制代码

Highlights:

Furture.get () Gets the value of the results depends on the state of execution, if the task is completed, the results will be returned immediately, otherwise the block until the task is completed into the state, and returns the result or throw an exception.

All state may end the "Run completed" indicates that the calculation, including the end of normal, due to cancellation due to abnormal ends and ends. When entering the completion status, he will stop in this state, as long as the state is not in the NEWstate, it means that the task has been completed.

FutureTask responsible for the computation result from the thread to perform tasks to call this thread's thread, and ensures the transfer security release process results UNSAFE lock-free programming technology to ensure the security thread - in order to maintain lock-free programming CPU consumption Therefore a state flag, to reduce CPU idle time pressure

  • Task deity: callable
  • Executive tasks: runner
  • Results of the task: outcome
  • Get the task result: state + outcome + waiters
  • Interrupted or canceled task: state + runner + waiters

run method

  1. Check the state, non-NEW, explained that it had started, direct return; otherwise, set runner for the current thread, the success will continue, otherwise, returns.
  2. Call Callable.call () method to perform the task, the success of the call set (result) method fails to call setException (ex) method, will eventually set state, and calls finishCompletion () method, blocking threads are awakened in the get () method them.
  3. As shown Note, if the variable is omitted ran, and the "set (result);" statements into the try block "ran = true;" statement at what will happen? First, the code logic point of view, there is no problem, however, take into account the "set (result);" method throws an exception even in case it is wrong? set () method calls to eventually done () User-defined method, therefore, can not be omitted.
  4. If the state is INTERRUPTING, the initiative to the CPU, the other thread executing the spin-wait interruption process. See handlePossibleCancellationInterrupt (int s) method.
public void run() {
        // UNSAFE.compareAndSwapObject, CAS保证Callable任务只被执行一次 无锁编程
        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 {
                    result = c.call(); // 执行任务
                    ran = true; // 成功
                } catch (Throwable ex) {
                    result = null; // 异常,清空结果
                    ran = false; // 失败
                    setException(ex); // 记录异常
                }
                if (ran) // 问题:ran变量可以省略吗,把set(result);移到try块里面?
                    set(result); // 设置结果
            }
        } finally {
            runner = null; // 直到set状态前,runner一直都是非空的,为了防止并发调用run()方法。
            int s = state;
            if (s >= INTERRUPTING) // 有别的线程要中断当前线程,把CPU让出去,自旋等一下
                handlePossibleCancellationInterrupt(s);
        }
    }
复制代码
      private void handlePossibleCancellationInterrupt(int s) {
         if (s == INTERRUPTING) // 当state为INTERRUPTING时
             while (state == INTERRUPTING) // 表示有线程正在中断当前线程
                 Thread.yield(); // 让出CPU,自旋等待中断
     }
复制代码

Under long-winded: run key units to do the following things:

将runner属性设置成当前正在执行run方法的线程
调用callable成员变量的call方法来执行任务
设置执行结果outcome, 如果执行成功, 则outcome保存的就是执行结果;如果执行过程中发生了异常, 则outcome中保存的就是异常,设置结果之前,先将state状态设为中间态
对outcome的赋值完成后,设置state状态为终止态(NORMAL或者EXCEPTIONAL)
唤醒Treiber栈中所有等待的线程
善后清理(waiters, callable,runner设为null)
检查是否有遗漏的中断,如果有,等待中断状态完成。
复制代码

How can less get way to do that has been blocked acquire See: awaitDone

    public V get() throws InterruptedException, ExecutionException {
        int s = state; // 执行器状态
         if (s <= COMPLETING) // 如果状态小于等于COMPLETING,说明任务正在执行,需要等待
             s = awaitDone(false, 0L); // 等待
         return report(s); // 报告结果
     }
复制代码

Incidentally secretly look get (Long, TimeUnit) , is to get a way to extend and increase the timeout after a timeout, I did not get angry Throws ....

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) // 如果状态小于等于COMPLETING,说明任务正在执行,需要等待;等待指定时间,state依然小于等于COMPLETING
            throw new TimeoutException(); // 抛出超时异常
        return report(s); // 报告结果
    }
复制代码

So look awaitDone , you know write an infinite loop while(true)|for (;;)is a master -

private int awaitDone(boolean timed, long nanos) throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L; // 计算deadline
        WaitNode q = null; // 等待结点
        boolean queued = false; // 是否已经入队
        for (;;) {
            if (Thread.interrupted()) { // 如果当前线程已经标记中断,则直接移除此结点,并抛出中断异常
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state; // 执行器状态
            if (s > COMPLETING) { // 如果状态大于COMPLETING,说明任务已经完成,或者已经取消,直接返回
                if (q != null)
                    q.thread = null; // 复位线程属性
                return s; // 返回
            } else if (s == COMPLETING) // 如果状态等于COMPLETING,说明正在整理结果,自旋等待一会儿
                Thread.yield();
            else if (q == null) // 初始,构建结点
                q = new WaitNode();
            else if (!queued) // 还没入队,则CAS入队
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
            else if (timed) { // 是否允许超时
                nanos = deadline - System.nanoTime(); // 计算等待时间
                if (nanos <= 0L) { // 超时
                    removeWaiter(q); // 移除结点
                    return state; // 返回结果
                }
                LockSupport.parkNanos(this, nanos); // 线程阻塞指定时间
            } else
                LockSupport.park(this); // 阻塞线程
        }
    }
复制代码

At this point, thread scheduling tasks, and I do not get long-winded ~ ~ ~ ~ still a lot to explore, after all, paid chat tight, I will not go into details -

queue

Then we look at the queue, in FutureTask, the queue is a singly linked list, it represents the set of all pending tasks completed execution thread. We know, FutureTask achieve the Future interface, you can get "Task" of the results, if you get the results, the task is not finished how to do it? Then the thread will get results in a pending queue waiting until the task is finished to be awakened. This is somewhat similar to the sync queue AQS our previous learning, and in the following analysis, we can control their similarities and differences of their own.

As we have said, the use of queues in concurrent programming is usually the current thread packaged into certain types of data structures thrown into the waiting queue, we take a look at the queue of each node is how a structure:

static final class WaitNode {
    volatile Thread thread;
    volatile WaitNode next;
    WaitNode() { thread = Thread.currentThread(); }
}
复制代码

Visible, compared to AQS is sync queuedoubly linked list used in Nodethis WaitNodebe simpler, it contains the next thread attributes and attribute points to a record thread of a node.

It is worth mentioning that, FutureTask in this way linked list is used as a stack to be exact stack to be used as Treiber, Treiber stack is Gesha do not understand can simply think of it as a thread-safe stack it is done using CAS push and pop operations (want to learn more, you can see this article). Why would we use a thread-safe stack, because the same time there may be multiple threads in obtaining the results of the task, if the task is still in the process of implementation, these threads will be packaged into WaitNode thrown Treiber stack stack top, complete stack operation, so there is the case of multiple threads simultaneously push may occur, thus requiring the use of thread-safe operations guarantee CAS pushed onto the stack, the stack is also the case for the same reason.

Since the queue is essentially a FutureTask Treiber(drive) stack, then the queue will only need to use a pointer to stack nodes on the line, in the FutureTask is waiters properties:

/** Treiber stack of waiting threads */
private volatile WaitNode waiters;
复制代码

In fact, it is the first node whole way linked list.

In summary, the queue structure FutureTask used as follows:

CAS operation

Most CAS operation is used to change the state, the FutureTask no exception. We generally static block of code required to initialize offset property CAS operations:

    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long stateOffset;
    private static final long runnerOffset;
    private static final long waitersOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = FutureTask.class;
            stateOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("state"));
            runnerOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("runner"));
            waitersOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("waiters"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
复制代码

From this static code block, we can also see, CAS operations for the three main attributes, including state, runner and waiters, illustrate the three basic attributes that will be simultaneously accessed by multiple threads. Which state property represents the state of the task, waiters property represents the node pointer points to the top, we have analyzed the two above before. runner property represents a thread of execution FutureTask the "Task" is. Why do we need a property to record a thread to perform tasks it? This is to interrupt or cancel the task to prepare, who knows only thread executing tasks that we can to interrupt it.

Finished defining attributes offset, next is the operation itself CAS. In FutureTask, CAS operation or the final call Unsafeclass compareAndSwapXXXmethod, on the Unsafe, since paid code text is not repeated here.

Practical exercise

一切没有例子的讲解都是耍流氓 >>> 起锅烧油


I git a problem - I go home at night to make up the actual code for

Guess you like

Origin juejin.im/post/5d3c46d2f265da1b9163dbce