5.介紹java.util.concurrent
Executor
是一個簡單的標準化介面,用於定義類似於執行緒的自定義子系統,包括執行緒池、非同步 IO 和輕量級任務框架。根據所使用的具體 Executor 類的不同,可能在新建立的執行緒中,現有的任務執行執行緒中,或者呼叫 execute() 的執行緒中執行任務,並且可能順序或併發執行。ExecutorService
提供了多個完整的非同步任務執行框架。ExecutorService 管理任務的排隊和安排,並允許受控制的關閉。ScheduledExecutorService
子介面及相關的介面新增了對延遲的和定期任務執行的支援。ExecutorService 提供了
安排非同步執行的方法,可執行由 Callable
表示的任何函式,結果類似於 Runnable
。Future
返回函式的結果,允許確定執行是否完成,並提供取消執行的方法。RunnableFuture
是擁有 run 方法的 Future,run 方法執行時將設定其結果。
ThreadPoolExecutor
和 ScheduledThreadPoolExecutor
提供可調的、靈活的執行緒池。Executors
類提供大多數 Executor 的常見型別和配置的工廠方法,以及使用它們的幾種實用工具方法。其他基於 Executor 的實用工具包括具體類 FutureTask
,它提供 Future 的常見可擴充套件實現,以及 ExecutorCompletionService
,它有助於協調對非同步任務組的處理。
佇列
java.util.concurrent ConcurrentLinkedQueue
類提供了高效的、可伸縮的、執行緒安全的非阻塞 FIFO 佇列。java.util.concurrent 中的五個實現都支援擴充套件的 BlockingQueue
介面,該介面定義了 put 和 take 的阻塞版本:LinkedBlockingQueue
、ArrayBlockingQueue
、SynchronousQueue
、PriorityBlockingQueue
和 DelayQueue
。這些不同的類覆蓋了生產者-使用者、訊息傳遞、並行任務執行和相關併發設計的大多數常見使用的上下文。BlockingDeque
介面擴充套件 BlockingQueue,以支援 FIFO 和 LIFO(基於堆疊)操作。LinkedBlockingDeque
類提供一個實現。
計時
TimeUnit
類為指定和控制基於超時的操作提供了多重粒度(包括納秒級)。該包中的大多數類除了包含不確定的等待之外,還包含基於超時的操作。在使用超時的所有情況中,超時指定了在表明已超時前該方法應該等待的最少時間。在超時發生後,實現會“盡力”檢測超時。但是,在檢測超時與超時之後再次實際執行執行緒之間可能要經過不確定的時間。接受超時期引數的所有方法將小於等於 0 的值視為根本不會等待。要“永遠”等待,可以使用 Long.MAX_VALUE 值。
同步器
四個類可協助實現常見的專用同步語句。Semaphore
是一個經典的併發工具。CountDownLatch
是一個極其簡單但又極其常用的實用工具,用於在保持給定數目的訊號、事件或條件前阻塞執行。CyclicBarrier
是一個可重置的多路同步點,在某些並行程式設計風格中很有用。Exchanger
允許兩個執行緒在 collection 點交換物件,它在多流水線設計中是有用的。
併發 Collection
除佇列外,此包還提供了設計用於多執行緒上下文中的 Collection 實現:ConcurrentHashMap
、ConcurrentSkipListMap
、ConcurrentSkipListSet
、CopyOnWriteArrayList
和 CopyOnWriteArraySet
。當期望許多執行緒訪問一個給定 collection 時,ConcurrentHashMap 通常優於同步的 HashMap,ConcurrentSkipListMap 通常優於同步的 TreeMap。當期望的讀數和遍歷遠遠大於列表的更新數時,CopyOnWriteArrayList 優於同步的 ArrayList。
此包中與某些類一起使用的“Concurrent&rdquo字首;是一種簡寫,表明與類似的“同步”類有所不同。例如,java.util.Hashtable 和Collections.synchronizedMap(new HashMap()) 是同步的,但 ConcurrentHashMap
則是“併發的”。併發 collection 是執行緒安全的,但是不受單個排他鎖的管理。在 ConcurrentHashMap 這一特定情況下,它可以安全地允許進行任意數目的併發讀取,以及數目可調的併發寫入。需要通過單個鎖不允許對 collection 的所有訪問時,“同步”類是很有用的,其代價是較差的可伸縮性。在期望多個執行緒訪問公共 collection 的其他情況中,通常“併發”版本要更好一些。當 collection 是未共享的,或者僅保持其他鎖時 collection 是可訪問的情況下,非同步 collection 則要更好一些。
大多數併發 Collection 實現(包括大多數 Queue)與常規的 java.util 約定也不同,因為它們的迭代器提供了弱一致的,而不是快速失敗的遍歷。弱一致的迭代器是執行緒安全的,但是在迭代時沒有必要凍結 collection,所以它不一定反映自迭代器建立以來的所有更新。
記憶體一致性屬性
Java Language Specification 第 17 章定義了記憶體操作(如共享變數的讀寫)的 happen-before 關係。只有寫入操作 happen-before 讀取操作時,才保證一個執行緒寫入的結果對另一個執行緒的讀取是可視的。synchronized
和 volatile
構造 happen-before 關係,Thread.start()
和Thread.join()
方法形成 happen-before 關係。尤其是:
- 執行緒中的每個操作 happen-before 稍後按程式順序傳入的該執行緒中的每個操作。
- 一個解除鎖監視器的(
synchronized
阻塞或方法退出)happen-before 相同監視器的每個後續鎖(synchronized
阻塞或方法進入)。並且因為 happen-before 關係是可傳遞的,所以解除鎖定之前的執行緒的所有操作 happen-before 鎖定該監視器的任何執行緒後續的所有操作。 - 寫入
volatile
欄位 happen-before 每個後續讀取相同欄位。volatile
欄位的讀取和寫入與進入和退出監視器具有相似的記憶體一致性效果,但不 需要互斥鎖。 - 線上程上呼叫
start
happen-before 已啟動的執行緒中的任何執行緒。 - 執行緒中的所有操作 happen-before 從該執行緒上的
join
成功返回的任何其他執行緒。
java.util.concurrent
中所有類的方法及其子包擴充套件了這些對更高階別同步的保證。尤其是:
- 執行緒中將一個物件放入任何併發 collection 之前的操作 happen-before 從另一執行緒中的 collection 訪問或移除該元素的後續操作。
- 執行緒中向
Executor
提交Runnable
之前的操作 happen-before 其執行開始。同樣適用於向ExecutorService
提交Callables
。 - 非同步計算(由
Future
表示)所採取的操作 happen-before 通過另一執行緒中Future.get()
獲取結果後續的操作。 - “釋放”同步儲存方法(如
Lock.unlock
、Semaphore.release
和CountDownLatch.countDown
)之前的操作 happen-before 另一執行緒中相同同步儲存物件成功“獲取”方法(如Lock.lock
、Semaphore.acquire
、Condition.await
和CountDownLatch.await
)的後續操作。 - 對於通過
Exchanger
成功交換物件的每個執行緒對,每個執行緒中exchange()
之前的操作 happen-before 另一執行緒中對應exchange()
後續的操作。 - 呼叫
CyclicBarrier.await
之前的操作 happen-before 屏障操作所執行的操作,屏障操作所執行的操作 happen-before 從另一執行緒中對應await
成功返回的後續操作。
Condition
Condition
將 Object
監視器方法(wait
、notify
和 notifyAll
)分解成截然不同的物件,以便通過將這些物件與任意 Lock
實現組合使用,為每個物件提供多個等待 set(wait-set)。其中,Lock
替代了 synchronized
方法和語句的使用,Condition
替代了 Object 監視器方法的使用。
條件(也稱為條件佇列 或條件變數)為執行緒提供了一個含義,以便在某個狀態條件現在可能為 true 的另一個執行緒通知它之前,一直掛起該執行緒(即讓其“等待”)。因為訪問此共享狀態資訊發生在不同的執行緒中,所以它必須受保護,因此要將某種形式的鎖與該條件相關聯。等待提供一個條件的主要屬性是:以原子方式 釋放相關的鎖,並掛起當前執行緒,就像 Object.wait
做的那樣。
Condition
例項實質上被繫結到一個鎖上。要為特定 Lock
例項獲得 Condition
例項,請使用其 newCondition()
方法。
作為一個示例,假定有一個繫結的緩衝區,它支援 put
和 take
方法。如果試圖在空的緩衝區上執行 take
操作,則在某一個項變得可用之前,執行緒將一直阻塞;如果試圖在滿的緩衝區上執行 put
操作,則在有空間變得可用之前,執行緒將一直阻塞。我們喜歡在單獨的等待 set 中儲存 put
執行緒和 take
執行緒,這樣就可以在緩衝區中的項或空間變得可用時利用最佳規劃,一次只通知一個執行緒。可以使用兩個 Condition
例項來做到這一點。
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
(ArrayBlockingQueue
類提供了這項功能,因此沒有理由去實現這個示例類。)
Condition
實現可以提供不同於 Object
監視器方法的行為和語義,比如受保證的通知排序,或者在執行通知時不需要保持一個鎖。如果某個實現提供了這樣特殊的語義,則該實現必須記錄這些語義。
注意,Condition
例項只是一些普通的物件,它們自身可以用作 synchronized
語句中的目標,並且可以呼叫自己的 wait
和notification
監視器方法。獲取 Condition
例項的監視器鎖或者使用其監視器方法,與獲取和該 Condition
相關的 Lock
或使用其 waiting
和 signalling
方法沒有什麼特定的關係。為了避免混淆,建議除了在其自身的實現中之外,切勿以這種方式使用Condition
例項。
除非另行說明,否則為任何引數傳遞 null
值將導致丟擲 NullPointerException
。
實現注意事項
在等待 Condition
時,允許發生“虛假喚醒”,這通常作為對基礎平臺語義的讓步。對於大多數應用程式,這帶來的實際影響很小,因為 Condition
應該總是在一個迴圈中被等待,並測試正被等待的狀態宣告。某個實現可以隨意移除可能的虛假喚醒,但建議應用程式程式設計師總是假定這些虛假喚醒可能發生,因此總是在一個迴圈中等待。
三種形式的條件等待(可中斷、不可中斷和超時)在一些平臺上的實現以及它們的效能特徵可能會有所不同。尤其是它可能很難提供這些特性和維護特定語義,比如排序保證。更進一步地說,中斷執行緒實際掛起的能力在所有平臺上並不是總是可行的。
因此,並不要求某個實現為所有三種形式的等待定義完全相同的保證或語義,也不要求其支援中斷執行緒的實際掛起。
要求實現清楚地記錄每個等待方法提供的語義和保證,在某個實現不支援中斷執行緒的掛起時,它必須遵從此介面中定義的中斷語義。
Executor CachedThreadPool FixedThreadPool SingleTreadExecutors
Executors.newCachedThreadPool(); //帶快取的 不夠時自動新增
Executors.newSingleThreadExecutor(); //單個執行緒池 執行緒死掉後自動建立
Executors.newFixedThreadPool(10); //建立容納N個執行緒的
Executors.newScheduledThreadPool(19); //建立定時器執行緒池
shutdown():用於關閉啟動執行緒,如果不呼叫該語句,jvm不會關閉。
awaitTermination():用於等待子執行緒結束,再繼續執行下面的程式碼。該例中我設定一直等著子執行緒結束。
Lock lock = new ReentrantLock(); //lock 互斥鎖 物件
Condition condition = lock.newCondition(); // 條件. 通訊物件
Condition //條件鎖
Semaphore //訊號量 類似執行授權 (最多有3個人可以走)
CyclicBarrier //類似集合點 (必須3個人同時到才能走)
CountDownLatch //計數器 計時器效果 某時間點同時執行 CountDownLath a = new CountDownLath(1); a.await(); a.countDown();
Exchanger //資料交換 Exchanger a = new Exchanger()//放主執行緒; a.exchange("asd")
ArrayBlockingQueue //阻塞佇列 ArrayBlockingQueue 啊 = new ArrayBlockingQueue(); a.put(1);//阻塞 a.add(1); //a.tack(); 獲取