《java併發程式設計的藝術》Executor框架

sayWhat_sayHello發表於2018-07-21

在HotSport VM執行緒模型中,java執行緒被一對一對映為本地作業系統執行緒。

分兩層:
頂層:Executor框架排程將多執行緒程式分解的若干任務對映為固定數量的執行緒。
底層:作業系統核心將這些執行緒對映到硬體處理器上。

Executor框架結構

  • 任務。包括被執行任務需要實現的介面:Runnable介面和Callable介面
  • 任務的執行。包括Executor,和ExecutorService介面(ThreadPoolExecutor和ScheduledThreadPoolExecutor)
  • 非同步計算的結果。包括介面Future和實現Future介面的FutureTask類

Executor框架的成員

  1. ThreadPoolExecutor通常由工廠類Executors建立,Executors能建立3中型別的ThreadPoolExecutor。
    1)FixedThreadPool。適用於負載比較重的伺服器。有兩個構造方法:
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newFixedThreadPool(int nThreads,ThreadFactory threadFactory)

本質上:

new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory);

2)SingleThreadPool。適用於需要保證順序執行各個任務;並且在任意時間點,沒有多執行緒活動的場景。兩個構造方法:

public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)

本質上:

new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory));

3)CachedThreadPool。大小無界,適用於執行很多的短期非同步任務的小程式,或負載較輕的伺服器。兩個構造方法:

public static ExecutorService newCachedThreadPool()
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)

本質上:

new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),threadFactory)
  1. ScheduledThreadPoolExecutor extends ThreadPoolExecutor.首先要了解,這個是繼承父類ThreadPoolExecutor的。
public ScheduledThreadPoolExecutor(int corePoolSize,ThreadFactory threadFactory,RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory, handler);
    }

1)ScheduledThreadPool適用於需要多個後臺執行緒執行執行週期任務,同時限制後臺執行緒個數。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)

本質上:

new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue())
new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue(),threadFactory)

2)SingleThreadScheduledExecutor適用於單個執行緒執行週期任務,保證順序。

public static ScheduledExecutorService newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)

本質上:

new DelegatedScheduledExecutorService(new ThreadPoolExecutor(1, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue()))
new DelegatedScheduledExecutorService(new ThreadPoolExecutor(1, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue(),threadFactory))
  1. Future介面
    Future介面和實現類FutureTask類用來表示非同步計算的結果。當我們呼叫submit方法時返回一個FutureTask物件。注意,在API中能看到返回的是一個實現Future介面的物件,將來可能會對Future實現類進行擴充。

  2. Runnable和Callable
    Runnable無返回,Callable返回Future。
    Executors能把Runnable包裝成Callable:

public static Callable<Object> callable(Runnable task)
public static <T> Callable<T> callable(Runnable task,T result)

ScheduledThreadPoolExecutor

主要用來在給定的延遲之後執行任務,或者定期執行任務。

DelayQueue是一個無界佇列。

執行分兩部分:
1. 當呼叫ScheduledThreadPoolExecute的scheduleAtFixedRate()方法或者scheduleWithFixedDelay()方法時,會向ScheduledThreadPoolExecute的DelayQueue新增一個實現了RunnableScheduleFuture介面的ScheduledFutureTask。
2. 執行緒池中的執行緒從DelayQueue中獲取ScheduledFutureTask,然後執行任務。

ScheduledFutureTask有3個成員變數:
- long型的time,表示這個任務將要被執行的具體時間。
- long型的sequenceNumber,表示這個任務被新增到ScheduledThreadPoolExecutor中的序號
- long型的period,表示任務執行的間隔週期。

DelayQueue封裝了一個PriorityQueue,這個PrioriyQueue會對佇列中的ScheduledFutureTask進行排序。排序時,time小的排在前面,time相同的則比較sequenceNumber,小的排前面。

執行步驟:
1. 從DelayQueue獲取已到期的ScheduleFutureTask(DelayQueue.take())。到期任務是指ScheduledFutureTask的time大於等於當前時間。
2. 執行ScheduledFutureTask
3. 修改ScheduledFutureTask的time為下次將要執行的時間
4. 把修改time後的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())

獲取任務3大步驟:
1. 獲取Lock
2. 獲取週期任務
- 如果佇列為空,當前執行緒到Condition中等待;否則執行下面一點
- 如果佇列的頭元素time時間比當前時間大,則到Condition中等待到time時間,否則執行下一點
- 獲取佇列的頭元素,如果佇列不為空,則喚醒Condition中等待的所有執行緒
3. 釋放Lock

FutureTask

FutureTask除了實現Future介面外,還實現Runnable介面。所以FutureTask可以交給Executor執行,也可以呼叫執行緒直接執行(FutureTask.run()).根據FutureTask.run()方法被執行的時機,FutureTask可以處於下面3種狀態:1. 未啟動 2. 已啟動 3. 已完成(正常完成、FutureTask.cancel()、拋異常結束)

FutureTask的實現基於AQS。每一個基於AQS實現的同步器都會包含兩種型別的操作:
1. 至少一個acquire操作。這個操作阻塞呼叫執行緒,除非/直到AQS的狀態允許這個執行緒執行。FutureTask中為get()/get(long timeout,TimeUnit unit)方法呼叫。
2. 至少一個release操作。這個操作改變AQS的狀態,改變後的狀態可允許一個或多個阻塞執行緒被解除阻塞。FutureTask中為run方法和cancel方法。

相關文章