JAVA異步編程-JDK中FutureTask實踐與原理

1.文章目錄

  1. Future接口概述
  2. FutureTask概述
  3. FutureTask顯式線程,線程池實現;
  4. FutureTask類結構,源碼導讀;
  5. FutureTask侷限性;

2.JDK 中Future

public interface Future<V> {

    // 取消任務
    boolean cancel(boolean mayInterruptIfRunning);

    // 任務是否取消
    boolean isCancelled();

    // 任務是否結束
    boolean isDone();

    // 獲取任務結果
    V get() throws InterruptedException, ExecutionException;

    // timeOunt時間內獲取結果
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}
  • V get() throws InterruptedException, ExecutionException;等待異步計算結果任務完成,返回結果;如果任務沒有完成,阻塞當前線程直到任務結束;如果等待任務結果的過程中有其他線程取消了任務,拋出CancellationException;被中斷拋出InteeuptionException異常;如果計算出現異常,拋出ExecutionException異常;
  •  V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;同get方法,多加了一個timeOut,如果等待任務結果時間超出timeOut則會拋出timeOunt異常;
  •  boolean isDone();如果任務計算完成返回true,否則false;
  • boolean cancel(boolean mayInterruptIfRunning);嘗試取消任務,如果當前線程已經完成任務/被其他線程取消,則嘗試取消任務失敗;如果任務還沒有執行,則任務就不會再執行;如果任務已經執行,根據mayInterruptIfRunning參數確定是否打斷正在運行的線程;
  •   boolean isCancelled();如果任務再執行完畢前被取消了,則該方法返回true;

3.FutureTask概述

  • FutureTask代表一個可被取消的異步計算任務,該類實現了Future接口,提供任務啓動,取消,查詢任務是否完成,獲取計算結果的接口;
  • FutureTask的結果只能等到任務完成纔可以獲取,使用get方法系列,當結果沒有出來,線程調用get系列方法會被阻塞。FutureTask的任務可以是Callable類型,也可以是Runnable接口;

4.顯式使用線程完成FutureTask任務

package AsynchronousProgramming;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @Author: SoftWareKang
 * @Name:JAVALEARN
 * @Date: 2020/6/1 19:38
 */
public class AsyncFutureExample {

    public static String doSomeThingA(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-- do someThing A--");

        return "TaskAResult";
    }

    public static String doSomethingB(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-- do someThing B--");

        return "TaskBResult";
    }

    public static void main(String[] argv) throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();

        // 創建FutureTask
        FutureTask<String> futureTask = new FutureTask<>(() -> {
            String result = null;
            try {
                result = doSomeThingA();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        });

        // 使用線程執行任務A
        new Thread(futureTask, "thredA").start();

        // 執行任務B
        String resultB = doSomethingB();

        // 等待futureTask結果
        String resultA = futureTask.get();

        System.out.println(resultA + ":"+ resultB);

        System.out.println(System.currentTimeMillis() - start);

    }
}

5.線程池執行FutureTask任務

package AsynchronousProgramming;

import java.util.concurrent.*;

/**
 * @Author: SoftWareKang
 * @Name:JAVALEARN
 * @Date: 2020/6/1 19:51
 */
public class AsncFutureTaskByThreadPool {
    // 獲取CPU數
    private final static int AVALIABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
    // 自定義線程池
    private final static ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(AVALIABLE_PROCESSORS, 2 * AVALIABLE_PROCESSORS,
            1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.CallerRunsPolicy());


    public static String doSomeThingA(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-- do someThing A--");

        return "TaskAResult";
    }

    public static String doSomethingB(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-- do someThing B--");

        return "TaskBResult";
    }

    public static void main(String[] argv) throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();

        // 創建FutureTask
        FutureTask<String> futureTask = new FutureTask<>(() -> {
            String result = null;
            try {
                result = doSomeThingA();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        });

        // 線程池執行
        POOL_EXECUTOR.execute(futureTask);

        // 執行任務B
        String resultB = doSomethingB();

        // 同步等待線程A結束
        String resultA = futureTask.get();
        // 打印結果
        System.out.println(resultA + ":" + resultB);
        System.out.println(System.currentTimeMillis() - start);

        // 關閉線程池
        POOL_EXECUTOR.shutdownNow();

    }
}

6.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;

    // 可執行任務
    private Callable<V> callable;
    // 任務運行結果
    private Object outcome; // non-volatile, protected by state reads/writes
    // 運行該任務的線程
    private volatile Thread runner;
    //無鎖棧,記錄等待任務結果的線程節點
    private volatile WaitNode waiters;

   // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    // state變量的偏移量
    private static final long stateOffset;
    // runner變量的偏移量
    private static final long runnerOffset;
    // waiters變量的偏移地址
    private static final long waitersOffset;
    static {
        try {
            // 獲取unsafe實例
            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);
        }
    }
  • 任務初始爲new;可以通過set,setExpection,cancel函數設置任務結果,任務會轉化爲終止狀態;
  • NEW->COMPLETING->NORMAL;正常終止流程轉化。
  • NEW->COMPLETING->EXCEPTIONAL:執行任務發生異常流程轉化;
  • NEW->CANCELLED:任務還沒開始就被取消;
  • NEW->INTERRUPTING->INTERRUPTED:任務被中斷;
  • 另外FutureTask使用UnSafe機制操作內存變量,記錄變量的偏移地址,方便後面CAS操作賦值;

WaitNode:一個列表,記錄被阻塞的鏈表;

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

構造函數:

 public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }
 static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }
  • 第二種初始化,方式用適配器模式做了轉化,Runnable->Callable

Run:任務的執行體,線程調用這個方法來運行具體任務,最後講結果賦值給outcome

public void run() {
        // 如果任務不是NEW,或者使用CAS設置Runner失敗,直接返回
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            // 如果任務不爲Null,state=new則執行任務
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    // 執行任務,設置ran爲true
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    // 異常
                    result = null;
                    ran = false;
                    setException(ex);
                }
                // 如果正常運行,則賦值
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            // 爲了讓調用cancle(true)的線程在該方法return前中斷
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

handlePossibleCancellationInterrupt:在返回前中斷;

 private void handlePossibleCancellationInterrupt(int s) {
       // 爲了讓其他線程中斷這個線程:不斷輪詢,讓出cpu使用權限
        if (s == INTERRUPTING)
            while (state == INTERRUPTING)
                Thread.yield(); // wait out pending interrupt
    }

setException方法:設置異常信息

 protected void setException(Throwable t) {
        // CAS設置state狀態爲COMPLETING
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            // outcome設置異常信息
            outcome = t;
            // 設置state狀態爲異常狀態
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            // 最後處理:waiter鏈表裏面的線程節點
            finishCompletion();
        }
    }

finishCompletion:激活鏈表裏面的線程節點

private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
           // 設置waiter爲NULL
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {
                    // 獲取頭節點
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        // 給予許可證,釋放
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }

        done();
        // callable置爲null
        callable = null;        // to reduce footprint
    }

set(V v)正常運行設置

 protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
             // 設置結果
            outcome = v;
            // 設置狀態爲正常
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

get方法:獲取任務結果,如果沒運行完,阻塞調用線程;

public V get() throws InterruptedException, ExecutionException {
        int s = state;
        // 如果狀態<=COMPLETING,表示還沒運行完
        if (s <= COMPLETING)
            // 調用awaitDone,任務終止
            s = awaitDone(false, 0L);
       // 返回結果
        return report(s);
    }
private int awaitDone(boolean timed, long nanos):
 private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        // 循環等待
        for (;;) {
            // 如果線程被中斷,則拋出異常
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw   new InterruptedException();
            }
            // 如果s》COMPLETING:表示任務已經執行完了
            int s = state;
            if (s > COMPLETING) {
                // q不爲null,置爲null
                if (q != null)
                    q.thread = null;
                return s;
            }
            // 如果任務狀態爲COMPLETING,釋放cpu
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            else if (q == null)
                // 創建wait節點
                q = new WaitNode();
            else if (!queued)
                // 如果沒有入隊,則插入waiter鏈表尾部,CAS方式
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                   q.next = waiters, q);
           //如果有超時設置,則LclSupport.parkNanos進行等待,超時拋出異常
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                // 阻塞
                LockSupport.park(this);
        }
    }

cancel(boolean myInterrruptIfRunning):myInterrruptIfRunning確定是否中斷正在執行的線程;

public boolean cancel(boolean mayInterruptIfRunning) {
        // 設置任務狀態
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            // 中斷
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            //清除鏈表節點
            finishCompletion();
        }
        return true;
    }
  • 到此重要的方法已經導讀完了

7.FutureTask侷限性

  • 爲了Future獲取結果,我們必須調用get方法,會阻塞調用線程,這不是很理想
  • 我們理想的:可以將多個異步結果變成一個;可以將一個的結果作爲下一個任務的參數;可以手動設置Future的結果;
  • 爲了克服這些問題:JDK8提出了CompletableFuture,後續對這個進行實踐&源碼導讀;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章