chapter21 Java多線程編程在Netty中的應用
1. Java內存模型與多線程編程
1.1 Java內存模型
- JVM規範定義了Java內存模型,來屏蔽各種操作系統、虛擬機實現廠商和硬件的內存訪問差異。
- 工作內存和主內存
- 所有變量都存儲在主內存中,每個線程都有自己獨立的工作內存,它保存了該線程使用的變量的主內存的複製。線程間的變量訪問需要通過主內存來完成。
- Java內存交互協議
- 定義了8種操作來完成主內存和工作內存的變量訪問
- lock: 主內存變量,把一個變量標識爲某個線程獨佔的狀態。
- unlock:主內存變量,把一個處於鎖定的變量釋放出來
- read:主內存變量,把一個變量的值從主內存傳輸到線程的工作內存中,以便隨後的load操作使用
- load:工作內存變量,把read讀取到的主內存中的變量值放入到工作內存的變量副本中
- use:工作內存變量,把工作內存變量的值傳遞給java虛擬機執行引擎,每當虛擬機遇到一個需要使用到變量值的字節碼指令時,將會執行該操作
- assign:工作內存變量,把從執行引擎接收到的變量的值賦給工作變量,每當虛擬機遇到一個變量賦值的字節碼指令時,將會執行該操作
- store:工作內存變量,把工作內存中的一個變量的值傳送到主內存中,以便隨後的write操作使用
- write:主內存變量,把store操作從工作內存中的變量值放入主內存的變量中
- Java的線程
- 目前操作系統實現線程的方式主要有三種:【sun的jdk,在windows和linux上採用內核線程的實現方式】
- 內核線程(KLT)實現:有內核來完成線程切換,並負責將線程任務映射到不同的處理器上
- 用戶線程實現(UT):用戶線程是指完全建立在用戶空間線程庫上的線程,用戶線程的創建、啓動、運行、銷燬和切換完全在用戶態中完成,不需要內核幫助,因此性能更高
- 混合實現:將內核線程和用戶線程混合在一起使用的方式
- 工作內存和主內存
2、Netty的併發編程實線
2.1 對共享的可變數據進行正確同步
2.2 正確使用鎖
- wait方法使線程等待某個條件,它必須在同步塊內部被調用,這個同步塊通常會鎖定當前對象實例。
- 始終使用wait循環來調用wait方法,永遠不要在循環之外調用wait方法。
- 喚醒線程,保守使用notifyAll,當處於等待的所有線程都在等待同一個條件,而每次只有一個線程可以從這個條件中被喚醒,那麼就用notify
2.3 volatile的正確使用
- volatile時java提供的最輕量級的同步機制,Java內存模型對volatile專門定了一個特殊的訪問規則,僅僅解決了可見性的問題
- 線程可見性
- 禁止指令重排序優化
- volatile最適合使用的是一個線程寫,其他線程讀的場合。
2.4 CAS指令和原子類
2.5 線程安全類的應用
- 線程池
- 併發集合
- 新的同步器
- 新的原子包裝類
2.6 讀寫鎖的應用
- ReadWriteLock使用場景:
- 讀多寫少
- 讀寫鎖是可重入、可降級的,一個線程獲取讀寫鎖後,可以繼續遞歸獲取;從寫鎖可以降級爲讀鎖,以便快速釋放鎖資源
- ReentrantReadWriteLock支持獲取鎖的公平策略
- 讀寫鎖支持非阻塞的嘗試獲取鎖,如果獲取失敗,直接返回false,而不是阻塞
- 獲取鎖之後一定要釋放鎖,否則會發生鎖溢出異常。
2.7 線程安全性文檔說明
2.8 不要依賴線程優先級
- Netty默認的線程工廠提供了實現類,開發了包含設置線程優先級字段的構造函數,這是個錯誤的決定。實際上JDK的優先級是無法跨平臺正確運行的。