Java併發和多執行緒:序

小雷FansUnion發表於2015-12-29
  最近,和不少公司的“大牛”聊了聊,其中很多是關於“併發和多執行緒”、“系統架構”、“分散式”等方面內容的。
不少問題,感覺自己表達的不夠清晰。
   這裡面就存在一個“典型問題”和“現象”了:能夠根據已有經驗和學習能力,把遇到的問題都解決,但是並不能很好地表達出來。
   
   生活中的很多事情,都是“相互選擇”“相互考察”的雙向問題,你站在“甲方”和“乙方”兩個立場,判斷往往是不同的。
   
   不能改變別人,就努力提高自己,使得自己有更多的選擇,抱怨這些“不公”是沒啥用處的,除了心裡舒服一點。
   
   為了解決這個問題,還是象往常一樣,通過一篇篇的文章和程式碼,總結經驗,寫點自己的見解。
   
   在解決一個技術問題,看到了JDK API文件中,關於java.util.concurrent併發包的總體介紹,拿來作為“序”還是挺不錯的。
   
   java.util.concurrent,在併發程式設計中很常用的實用工具類,介面有10多個,實現類有20多個。 
   此包包括了幾個小的、已標準化的可擴充套件框架,以及一些提供有用功能的類,沒有這些類,這些功能會很難實現或實現起來冗長乏味。
   
   下面簡要描述主要的元件,另請參閱 locks 和 atomic 包。 


執行程式
介面。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 成功返回的後續操作。 


  對於“happen-before”等地方,還沒完全搞懂,需要時間消化。
   可參考:http://ifeve.com/easy-happens-before/,http://mapserver000-gmail-com.iteye.com/blog/1945688
  
  江湖傳言,《Java併發程式設計實踐》這本書還是很不錯的,2016年買本讀讀,最近先多研究點程式碼層次的東東。
   
  

相關文章