線程池-工作單元


1.Runnable接口

The Runnable interface should be implemented by any class 
whose instances are intended to be executed by a thread. The 
class must define a method of no arguments called run.

This interface is designed to provide a common protocol for 
objects that wish to execute code while they are active. For 
example, Runnable is implemented by class Thread. Being active 
simply means that a thread has been started and has not yet 
been stopped.

In addition, Runnable provides the means for a class to be active 
while not subclassing Thread. A class that implements Runnable 
can run without subclassing Thread by instantiating a Thread 
instance and passing itself in as the target. In most cases, the 
Runnable interface should be used if you are only planning to 
override the run() method and no other Thread methods. This is 
important because classes should not be subclassed unless the 
programmer intends on modifying or enhancing the fundamental 
behavior of the class.

Runnable接口由如下類實現:該類需要被線程執行。該類需要定義一個無參方法run。

Runnable提供了一種不是繼承Thread的方式。在大多數情況下,如果僅僅是覆寫run方法而不是其他Thread方法,則應該使用Runnable接口。除非打斷修改或增強類的基本行爲,否則不應該對類進行子類化。

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

2.Callable接口

A task that returns a result and may throw an exception. 
Implementors define a single method with no arguments called 
call.

The Callable interface is similar to Runnable, in that both are 
designed for classes whose instances are potentially executed by 
another thread. A Runnable, however, does not return a result 
and cannot throw a checked exception.

The Executors class contains utility methods to convert from 
other common forms to Callable classes.

返回結果或拋出異常的任務,實現者定義一個無參call方法。

與Runnable接口類似,該類實例可能由另一個線程執行。但是Runnable不能返回結果或拋出受查異常。

Executors類包含將其他常用形式轉換爲Callable類的實用方法。

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

3.Future

A Future represents the result of an asynchronous computation.
 Methods are provided to check if the computation is complete, to 
wait for its completion, and to retrieve the result of the 
computation. The result can only be retrieved using method get 
when the computation has completed, blocking if necessary until 
it is ready. Cancellation is performed by the cancel method. 
Additional methods are provided to determine if the task 
completed normally or was cancelled. Once a computation has 
completed, the computation cannot be cancelled. If you would 
like to use a Future for the sake of cancellability but not provide a 
usable result, you can declare types of the form Future<?> and 
return null as a result of the underlying task.

Future表示異步計算的結果。提供檢查計算是否完成、等待計算完成以及取出計算結果的方法。只有在計算完成時,get方法才能取到結果,必要時會阻塞直到其計算完成。cacel方法執行取消動作。提供了其他方法檢測任務是正常完成還是被取消。當計算完成後,其不能被取消。如果爲了取消而不是提供有用結果而使用Future,可以聲明Future<?>形式的類型,並返回null。

示例用法(如下的類都是虛構的):

 interface ArchiveSearcher { String search(String target); }
 class App {
   ExecutorService executor = ...
   ArchiveSearcher searcher = ...
   void showSearch(final String target)
       throws InterruptedException {
     Future<String> future
       = executor.submit(new Callable<String>() {
         public String call() {
             return searcher.search(target);
         }});
     displayOtherThings(); // do other things while searching
     try {
       displayText(future.get()); // use future
     } catch (ExecutionException ex) { cleanup(); return; }
   }
 }

FutureTask類是Future的一個實現,並實現了Runnable,因此可以由Executor執行。上述帶submit結構可由下面的形式代替:

 FutureTask<String> future =
   new FutureTask<String>(new Callable<String>() {
     public String call() {
       return searcher.search(target);
   }});
 executor.execute(future);

如下爲Future的完整形式:

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

關於cancle方法的說明:

  • 在任務已經完成、已經被取消或者由於其他原因不能被取消時,取消會失敗。
  • 任務還未啓動時調用cacel,會成功,並且任務永遠都不能運行
  • 任務已經啓動了,mayInterruptIfRunning 參數會決定執行該任務的線程是否會被中斷,以中斷該任務
  • 該方法返回後,後續的isDone()調用總是返回true。如果該方法返回true,後續的isCancelled()總是返回true。

4.RunnableFuture

A Future that is Runnable. Successful execution of the run 
method causes completion of the Future and allows access to its 
results.

是Runnable的Future,成功執行run方法將使Future完成,並允許訪問其他結果。

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

5.FutureTask

A cancellable asynchronous computation. This class provides a 
base implementation of Future, with methods to start and cancel 
a computation, query to see if the computation is complete, and 
retrieve the result of the computation. The result can only be 
retrieved when the computation has completed; the get methods 
will block if the computation has not yet completed. Once the 
computation has completed, the computation cannot be restarted 
or cancelled (unless the computation is invoked using 
runAndReset()).

A FutureTask can be used to wrap a Callable or Runnable object. 
Because FutureTask implements Runnable, a FutureTask can be 
submitted to an Executor for execution.

In addition to serving as a standalone class, this class provides 
protected functionality that may be useful when creating 
customized task classes.

可取消的異步計算。此類提供了Future的基本實現,包括啓動和取消計算的方法,查詢計算是否完成的查詢,以及檢索計算結果。 只有在計算完成後才能檢索結果; 如果計算尚未完成,get方法將阻塞。 計算完成後,無法重新啓動或取消計算(除非使用runAndReset()調用計算)。

FutureTask可用於包裝Callable或Runnable對象。 因爲FutureTask實現了Runnable,所以可以將FutureTask提交給Executor執行。

除了作爲獨立類之外,此類還提供了在創建自定義任務類時可能有用的保護方法。

Revision notes: This differs from previous versions of this
class that relied on AbstractQueuedSynchronizer, mainly to
avoid surprising users about retaining interrupt status during
cancellation races. Sync control in the current design relies
on a "state" field updated via CAS to track completion, along
with a simple Treiber stack to hold waiting threads.

Style note: As usual, we bypass overhead of using
AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.

修訂說明:之前版本依賴於AQS,現版本主要是爲了避免在取消爭用期間保留中斷狀態。當前設計中的同步控制依賴於通過CAS更新state域來跟蹤完成,以及用於保存等待線程簡單的Treiber棧。

樣式說明:像之前一樣,繞過了使用AtomicXFieldUpdaters的開銷,而是直接使用Unsafe內部方法。

5.1 狀態的變化

    /**
     * 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;

任務的運行狀態初始化爲NEW。任務狀態轉爲終止狀態只會發生在方法set setException和cancel方法。在完成期間,state可能會一個短暫的COMPLETING或者INTERRUPTING。從這些中間狀態到最終狀態的轉換使用更便宜的ordered/惰性寫入,因爲值是唯一的並且不能進一步修改。

可能的狀態轉換:

  • NEW -> COMPLETING -> NORMAL
  • NEW -> COMPLETING -> EXCEPTIONAL
  • NEW -> CANCELLED
  • NEW -> INTERRUPTING -> INTERRUPTED

5.2 構造器及域

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

如果不需要結果,考慮使用下面的形式:
Future<?> f = new FutureTask<Void>(runnable, null);

從構造器可以看出,任務的初始狀態爲NEW。

    /** 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;
  • callable代表執行的任務,運行後置爲null;
  • outcome代表返回結果或者要拋出的異常,從get()獲取的
  • runner代表運行callable的線程,在run()中CAS賦值
  • waiters代表等待線程的Treiber棧

5.3 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 {
                    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
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
  • step1.線程狀態不爲NEW時返回;設置任務的runner爲當前線程
  • step2.運行任務result = c.call()
    正常運行完成後,set(result)
    發生異常,setException(ex)
  • step3.運行完成後,設置runner = null
    重新讀取state以防有漏掉的中斷:s >= INTERRUPTING時,會調用handlePossibleCancellationInterrupt(s)

runner作爲鎖使用,防止其他線程併發調用run:

  • run之前首先通過CAS嘗試獲得鎖,並將runner置爲當前線程
  • run之後通過將runner = null釋放鎖
    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
    protected void setException(Throwable t) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }

從上面可以看出,run有兩條狀態轉移路線:

  • 1)正常運行set(result)
    NEW -> COMPLETING -> NORMAL
  • 2)發生異常
    NEW -> COMPLETING -> EXCEPTIONAL

關於finishCompletion:

    private void finishCompletion() {
        // assert state > COMPLETING;
        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);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }

        done();

        callable = null;        // to reduce footprint
    }
  • step1.將waiters置爲null
  • step2.逐個喚醒在棧中阻塞的線程LockSupport.unpark(t)
  • step3.調用保護方法done(),子類可以實現已完成想要的功能
  • step4.將callable置爲null

5.4 get

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
  • step1.如果任務還沒有完成,則調用awaitDone
  • step2.任務完成,則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 (;;) {
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }

            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) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }
  • step1.如果發生中斷,則removeWaiter(q),並拋出異常
  • step2.如果已經完成,則返回狀態
  • step3.狀態爲COMPLETING(表示已經執行完了,正在最後的設置期間),則調用Thread.yield(),因爲當前線程是get,一般是另外的線程在執行任務,所以讓執行任務的線程優先獲得執行權限
  • step4.狀態爲< COMPLETING,此時任務還在執行,則創建q = WaitNode()
  • step5.如果創建的q沒有入隊,則入隊,並置於隊首,即入棧
  • step6.最當前線程進行進行阻塞
    static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }

5.5 cancel

    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;
    }
  • step1.不是NEW狀態,直接返回false,表示此時任務已經執行完了
  • step2.爲NEW狀態,如果 mayInterruptIfRunning 爲false
    NEW - > CANCELLED
  • step3.爲NEW狀態,如果 mayInterruptIfRunning 爲true
    NEW -> INTERRUPTING -> INTERRUPTED
    並會對運行任務的runner進行中斷

6.爲什麼FutureTask不再基於AQS

ThreadPoolExecutor executor = ...;  
executor.submit(task1).cancel(true);  
executor.submit(task2);  

雖然中斷的是task1,但可能task2得到中斷信號。

JDK1.6的 FutureTask.Sync.innerCancel的代碼:

boolean innerCancel(boolean mayInterruptIfRunning) {  
 for (;;) {  
  int s = getState();  
  if (ranOrCancelled(s))  
      return false;  
  if (compareAndSetState(s, CANCELLED))  
      break;  
 }  
    if (mayInterruptIfRunning) {  
        Thread r = runner;  
        if (r != null)  //1
            r.interrupt(); //2
    }  
    releaseShared(0);  
    done();  
    return true;  
}  

按照如下的執行流程,task2得到中斷信號:

  • step1.主線程調用cancel(true)想取消task1,執行完1處檢查後,停住
  • step2.Thread1執行完task1,開始執行task2
  • step3.主線程此時繼續執行2處的r.interrupt(),那麼task2將會被中斷

看看新版本怎麼處理這個中斷遺留問題:
cancel代碼:

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

run最後的代碼:

        } 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
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    /**
     * Ensures that any interrupt from a possible cancel(true) is only
     * delivered to a task while in run or runAndReset.
     */
    private void handlePossibleCancellationInterrupt(int s) {
        // It is possible for our interrupter to stall before getting a
        // chance to interrupt us.  Let's spin-wait patiently.
        if (s == INTERRUPTING)
            while (state == INTERRUPTING)
                Thread.yield(); // wait out pending interrupt

        // assert state == INTERRUPTED;

        // We want to clear any interrupt we may have received from
        // cancel(true).  However, it is permissible to use interrupts
        // as an independent mechanism for a task to communicate with
        // its caller, and there is no way to clear only the
        // cancellation interrupt.
        //
        // Thread.interrupted();
    }

可知run最後對此進行了特別處理:

  • 如果該任務已經被取消,且未被取消完成,則handlePossibleCancellationInterrupt(s)
  • handlePossibleCancellationInterrupt其實很簡單,就是在主線cancel()完成之前,在這裏自旋,Thread.yield()讓執行取消的主線程更容易獲得執行機會。

這裏根本就是在主線程執行取消指定任務時,讓執行該取消任務的線程自旋等待 主線程中斷操作 完成。

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章