2. 安全性
竟態條件(Race Condition);計算結果正確性取決於多個線程交替執行時序時,那麼就會發生竟態條件;java
- 安全性 不發生任何錯誤的行爲
- 活躍性 某個正確的事情最終會發生
如何修復線程安全問題:web
- 1.不在線程間共享該狀態變量
- 2.將狀態變量改成不可修改
- 3.在訪問狀態變量是使用同步機制;
關於對象安全性總結算法
- 1.無狀態的對象必定是線程安全的;
- 2.在併發線程中,保持狀態的一致性,就是原子性;
- 3.每一個共享和可變的變量都應該由鎖來保護,維護人員應該知道是哪個鎖;
- 4.涉及到包含多個變量的不變性條件,其中涉及的全部變量都須要同一個鎖來保護.
不良併發數據庫
3.對象的共享
3.1 可見性
重排序windows
3.1.1 失效數據
3.1.2 非原子的64位操做
3.1.3 加鎖與可見性
加鎖不只保證互斥還保證可見性數組
3.1.4 volatile變量
volatile僅能保證可見性,不能保證原子性; 知足如下條件:緩存
- 1.對變量的寫入操做不依賴變量的當前值,或者只有單個線程更新變量的值;
- 2.該變量不會與其餘變量一塊兒歸入不變性條件中
3.2發佈和溢出;
- 發佈:對象能在當前做用域以外的代碼中使用
- 溢出:不該該發佈的對象發佈就叫溢出;
安全發佈安全
- 1.將對象保存到共有的靜態變量中
- 2.從非私有方法返回一個引用
- 3.發佈一個內部類的實例 需防止對象的this沒有溢出;
3.3線程封閉
3.3.1 Ad-hoc線程封閉
線程封閉的職責由線程實現來承擔;啥都不加;服務器
3.3.2 棧封閉
局部變量網絡
3.3.3 ThreadLocal
線程的全局變量,在線程內部共享,線程結束後回收;
3.3.4 不變對象
1.建立後狀態不能被修改 2.對象全部域都是final類型 3.對象正確建立.()
3.4 不變性
不可變的對象必定是線程安全的
3.4.1 final域
3.4.2 使用volatile發佈不可變域
3.5 安全發佈
3.5.1 不正確的發佈:正確的對象被破壞
3.5.2 不可變對象與初始化安全性
3.5.3 安全發佈的經常使用模式
- 1.在靜態初始化函數中初始化一個對象;
- 2.將對象保存到volatile類型的域中或AtomaticReference對象中
- 3.將對象的引用保存到某個正確構造對象的final類型域中
- 4.將對象的引用保存到一個由鎖保護的域中 共享對象的策略:線程封閉,只讀共享,線程安全共享,保護對象;
4 對象的組合
經過對象組合設計出更大的組件或程序
4.1 設計線程安全的類
包含如下幾點:
- 1.找出構成對象狀態的全部變量;
- 2.找出約束狀態變量的不變性條件.
- 3.創建對象狀態的併發訪問管理策略. 同步策略:在不違背對象不變條件和後驗條件性的前提對其狀態訪問進行協同
4.1.1 收集同步需求
4.1.2 依賴狀態的操做
須要加鎖
4.1.3 狀態全部權
通常對象,容器類對象;發佈對象;
4.2線程封閉
封閉在做用域,線程,私有成員;
4.3線程安全性的委託
即便都是線程安全的對象,組成在一塊兒也不必定是線程安全的對象組合;
4.3.1 在容器裏的變量
4.3.2 獨立的狀態變量
4.3.3 委託失效
不符合先驗條件,錯誤使用了volatile
4.3.4 發佈底層的狀態變量
4.4在現有的線程安全類中添加功能
4.4.1 客戶端加鎖機制
不推薦
4.4.2 組合
推薦
4.5將同步策略文檔化
將哪些變量聲明爲volatile類型,哪些變量用鎖保護,哪些鎖保護哪些變量,那些變量必須是不可變的或者被封閉到線程中的,那些操做必須是原子操做; 最好在接口上書寫線程安全的文檔
5.基礎構建模式
5.1 同步容器類
5.1.1 同步容器類的問題
迭代,跳轉,條件計算都會出現相似問題
5.1.2 迭代器與concurrentModificationException
避免該狀況需對容器加鎖
5.1.3 隱藏迭代器
5.2 併發容器
- concurrentHashMap;
- queue:concurrentLinkedQueue,PriorityQueue;
- concurrentSkipListMap(替代sortMap),ConcurrentSkipListSet(替代sortSet); blockingQueue;
5.2.1 ConcurrentHashMap
5.2.2 concurrentHashMap擴展的接口
- V putIfAbsent(K key,V value); 沒有值才能插入
- boolean remove(K key,V value); 僅當k被V映射時才能移除
- boolean replace(K key,V oldValue,V newValue); 僅當K被映射到oldValue時才替換爲newValue
- V replace(K key,V newValue);
5.2.3 CopyOnWriteArrayList
寫入時複製;適用於寫入少讀取多的狀況
5.3 阻塞隊列和生產者-消費者模式
put,get;定時的offer和poll, 相似線程池和工做隊列的關係; LinkedBlockingQueue,ArrayBlockingQueue:FIFO; PriorityBlockingQueue; 足夠多的的消費者,有一個生產者時,才能使用同步隊列;
5.3.2 串行線程封閉
確保對象只有一個線程獲取;
5.3.3 雙端隊列和工做密取
deque,blockingDeque;密取時從隊尾去獲取線程;
5.4 阻塞方法與中斷方法
傳遞或恢復中斷
5.5 同步工具類
5.5.1 閉鎖(CountDownLatch)
await(),countDown();
5.5.2 futureTask(必須執行完成)
futureTask.get()會拋出異常Throwable,須要處理;
5.5.3 信號量
Semaphore; acquire()獲取信號,release()釋放信號
5.5.4 柵欄
Barrier,CyclicBarrier; await();當柵欄剩餘數量達到0時,會通知主線程執行;
6.任務執行
6.1在線程中執行任務
清晰的任務邊界,明確的任務執行策略
6.1.1 串行的執行任務
6.1.2 顯式的爲任務建立線程
6.1.3 無限制建立線程的不足
- 1.線程生命週期的開銷很是高.
- 2.資源消耗.線程閒置,佔用內存.
- 3.穩定性.易被攻擊;
6.2 Executor框架
基於簡單的生產者消費者框架;使用Runnable執行任務;
6.2.1 示例:基於Executor的web服務器
6.2.2 執行策略
包括:什麼線程,執行順序,併發數量,等待數量,拒絕方案;任務完成先後的事件
6.2.3 線程池
好處:
- 1.節省線程建立銷燬的開銷;
- 2.提升線程響應
- 3.防止線程相互競爭致使oom
建立方法: newFixedThreadPool,newCachedThreadPool,newSingleThreadExecutor,newScheduledThreadPool;
6.2.4 關閉任務池
shutdown(),isShutdown();shutdownNow();isTerminated(),awaitTermination; ExecutorService的生命週期:運行,關閉,終止;
6.2.5 延遲任務與週期任務
使用ScheduledThreadPool來代替Timer執行
6.3 找出可利用的並行性
6.3.1 實例:串行的頁面渲染器
6.3.2 攜帶結果的任務callable與Future
- Future能夠查看任務狀態,取消任務,獲取任務結果;
- boolean cancel(boolean mayInterruptRnning);
- boolean isCancelled();
- boolean isDone();
- v get();
- v get(long timeout,TimeUnit,unit);
6.3.3 使用future實現頁面渲染器
6.3.4 在異構任務並行化中存在的侷限
當大量異構任務能夠併發處理時,才能帶來性能真正的提高;
6.3.5 CompletionService: Executor與BlockingQueue
多個completionService可共享一個Executor;將結果存入blockingQueue
6.3.7 爲任務設置時限
若是超過期限,報錯TimeOutException,則應該當即終止或取消該任務;
6.3.8 示例:旅行預訂門戶網站
executorService.invokeAll(List<Callable>,time,unit); 所有定時執行
7.取消和關閉
運行良好的軟件能處理好失敗,關閉,取消等過程;
7.1 任務取消
用戶請求取消,有時間限制的操做,應用程序事件,錯誤,關閉;
7.1.1 中斷
使用interrupt中斷
7.1.2 中斷策略
須要捕獲了InterruptedException以後恢復中斷狀態
7.1.3 響應中斷
7.1.5 經過future來實現取消
future.cancel();
7.1.6 處理不可中斷的阻塞
同步socket I/O,同步I/O;
7.2 中止基於線程的服務
若是服務的時間大於線程存在的時間,那就應該提供關閉服務的方法
7.2.1 示例:日誌服務
7.2.2 關閉ExecutorServie
shutdown()關閉,shutdownNow
7.2.3 毒丸對象
7.2.4 只執行一次的服務
shutdown(),awaitTermination(timeout,unit);
7.2.5 shutdownNow的侷限性
7.3 處理非正常的線程終止
未捕獲的異常;只有execute()執行中拋出的異常纔是未捕獲異常;
7.4 jvm關閉
7.4.1 關閉鉤子
正常的關閉:關閉鉤子->if(finalizer),執行finalizer; Runtime.getRuntime().addShutdownHook(Thread);註冊關閉鉤子
7.4.2 守護線程
守護線程在jvm中止時,會直接拋棄,不會執行finally代碼塊
7.4.3 終結器
只適用於管理那些資源是經過本地方法獲取的對象;
8 線程池的使用
8.1 在任務與執行策略之間的隱性耦合
依賴性任務;線程封閉的任務;對響應時間敏感的任務,使用ThreadLocal的任務;
8.1.1 線程飢餓死鎖
依賴性任務,依賴相關任務的完成
8.1.2 運行時間較長的任務
可堵塞方法的現時不限時版本:Thread.join,BlockingQueue.put,CountDownLatch.await,Selector.select;
8.2 設置線程池的大小
- 過大會競爭,太小會閒置;
- 計算密集型:通用:cpu個數+1;
- 更精細的判斷: cpu個數cpu利用率(1+計算等待時間/計算時間)
8.3 配置ThreadPoolExecutor
new ThreadPoolExecutor(args...)
8.3.1 線程的建立和銷燬
- 基本容量
- 最大容量
- 存活時間; 超過存活時間,會被回收;
fixedThreadPool線程池基本容量大小和最大容量同樣大; newCachedThreadPool的線程池基本容量爲0,最大容量爲Integer.MAX_VALUE;
8.3.2 管理隊列任務
-
用blockingQueue來保存等待任務;包括:無界隊列,有界隊列,同步移交
-
fixedThreadPool,singleThreadExecutor使用無界的LinkedBlockingQueue;
-
synchronousQueue 適合無界的線程池,好比cachedThreadPool
-
PriorityBlockingQueue,根據優先級排序;
-
線程依賴性,則選擇CachedThreadPool;
8.3.3 飽和策略
經過setRejectedExecutionHandler(ThreadPoolExecutor.CallerRunsPolicy)來修改
-
終止(abort);默認飽和策略;拋出RejectExecutionException;注意拋棄最舊的策略與優先級隊列(Discaord-Oldest)
-
調用者運行策略; 超出後在主線程執行該任務,這段時間不接受新的任務,其餘任務提交到tcp層的隊列中;
8.3.4 線程工廠
- 實現ThreadFactory接口
- 設計本身的Thread類; extend Thread;
8.3.5 在調用構造函數後再定製 ThreadPoolExecutor
- 將ExecutorService轉型爲ThreadPoolExecutor進行設置;
- unconfigurableExecutorService;返回一個代理,且不能對他設置
8.4 擴展 ThreadPoolExecutor
可重寫的方法:beforeExecute,afterExecute(任務完成帶有Error,則不執行),terminated;
8.5 遞歸算法的並行化
- 循環算法可使用並行化
- 遞歸循環也可使用並行化技術
- 使用Latch(閉鎖)來接受答案,控制整個流程;使用concurrentHashMap來控制保存遍歷印記避免重複循環;
9 GUI使用 略;
第三部分 活躍性,性能與測試
10.避免活躍性的危險
- 順序死鎖(Lock-Ordering deadlock)
- 資源死鎖(Resource deadlock)
10.1 死鎖
死鎖狀況:每一個人都擁有其餘人須要的資源,同時又等待其餘人已經擁有的資源,而且每一個人在獲取全部須要的資源以前都不會放棄已經擁有的資源;
10.1.1 鎖順序死鎖
兩個線程以不一樣的順序獲取相同的鎖
10.1.2 動態鎖的順序死鎖
- 兩個鎖,可能會相互交換.須要有序的調用鎖.
- System.identityHashCode(Object)獲取hash值.
- 比較hash值大小,肯定加鎖順序
- 若是相等,則先獲取第三方變量(中立者)的鎖;
10.1.3 在協助對象之間發生的死鎖
若是持鎖時調用外部方法,那麼將出現活躍性問題.若是外部方法須要獲取其餘的鎖,就可能致使死鎖產生.
10.1.4 開放調用
- 在調用某方法時不須要持有鎖,那麼就叫開放調用;
- 收縮同步代碼塊的保護範圍
- 服務關閉;服務關閉期間一直持有服務的鎖,直到關閉完成才釋放鎖;
10.1.5 資源死鎖
- 資源相互等待死鎖
- 線程飢餓死鎖; 線程A依賴線程B的執行結果
10.2 死鎖的避免和診斷
- 儘可能減小潛在的加鎖交互的數量,將獲取鎖時須要遵循的協議寫入正式文檔並始終遵循這些協議
- 兩階段策略:找出獲取多個鎖的地方,對這些實例進行全局分析,確保他們在整個程序中獲取鎖的順序都保持一致;
10.2.1 支持定時的鎖
10.2.2 經過線程轉儲信息來分析死鎖
10.3 其餘活躍性危險
飢餓,丟失信號,活鎖
飢餓
糟糕的響應性
後臺任務與前臺任務的競爭;大容器的迭代
10.3.3 活鎖
- 重複執行,排在消息隊列的頭部,但沒法完成執行
- 過分的代碼恢復機制
- 須要在重試機制添加隨機性
11.性能和可伸縮性
應該保證程序正確運行,在提高程序性能
11.1對性能的思考
- 受限:cpu,io,磁盤,內存,帶寬,數據庫請求,
- 線程額外開銷:線程之間協調(加鎖,觸發信號,內存同步),上下文切換,線程建立和銷燬,線程調度;
11.1.1 性能與可伸縮性
性能:
- 運行速度(服務時間,等待時間)
- 處理能力(生產量,吞吐量)
可伸縮性:增長計算資源時,程序吞吐量或處理能力相應的增長;
11.1.2 評估各類性能權衡因素
Amdahl 定律
按母達爾定律,增長處理器得到的最高加速比,這個值取決於程序中可並行組件與串行組件所佔的比例;
Speedup<= 1/(F+(1-F)/N)
F指必須串行程序佔的比例,N指處理器數量;理論最大值是,1/F
- 全部的併發程序都包含串行的部分,若是你的程序沒有,則須要仔細檢查一下;
11.2.2 Amdahl定律的應用
找到加速比;串行代碼
線程引入的開銷
11.3.1 上下文切換
unix系統的vmstat命令和windows系統的perfmon工具能夠報告上下文切換次數及在內核中執行時間所在比例;
11.3.2 內存同步
- 內存柵欄會刷新緩存,使緩存無效,刷新硬件的寫緩存 jvm會進行一些優化,去掉無用的同步;
- 對synchronzed對無競爭的同步作了優化
- 略過不會發生競爭的鎖
- 經過溢出分析找出線程本地變量,他們不會進行同步加鎖操做;
- 鎖粒度粗化操做,把臨近的同步代碼塊用一個鎖合併起來
11.3.3 阻塞
- 競爭鎖失敗會自旋或掛起
- 掛起包含兩次上下文切換開銷,必要的操做系統操做和緩存操做
11.4 減小鎖的競爭
- 減小鎖的持有時間
- 下降鎖的請求頻率
- 使用帶有協調機制的獨佔鎖,這些機制容許更高的併發性'
11.4.1 縮小鎖的範圍(快進快出)
- 把一個同步代碼塊分解爲多個時,並不能提高性能,jvm優化
- 只有把一些大量的計算和阻塞IO移出才能夠
11.4.2 鎖分解
11.4.3 鎖分段
11.4.4 避免熱點域
- 以計數器爲例,每一個元素髮生變化都會訪問,這就是一個熱點域.
- 經過計數分段的方法實現鎖分段
11.4.5 一些替代獨佔鎖的方法
- readWriteLock;讀取多,寫入少
- 原子變量下降熱點域; 靜態計數器,序列發生器,鏈表頭節點引用
11.4.6 檢測cpu的利用率
負載不均勻的緣由
- 負載不充足
- I/O 密集
- 外部限制,網絡限制
- 鎖競爭
11.4.7 向對象池說不
11.5 實例:比較map的性能
11.6 減小上下文切換的開銷
當日志消息轉到另外一個線程進行處理
12 併發程序的測試
- 安全性,活躍性兩點;
- 活躍性,包括進展測試,無進展測試兩點
- 性能測試:吞吐量,響應性,可伸縮性
12.1 正確性測試
12.1.1 基本的單元測試
12.1.4 資源管理的測試
容器應該及時清除無用的對象;
12.1.5 使用回調
12.1.6 產生更多的交替操做
12.2 性能測試
12.2.1 在putTakeTest中增長計時功能
12.3 避免性能測試的陷阱
12.3.1 垃圾回收
12.3.2 動態編譯
12.3.3 對代碼路徑的不真實採樣
12.3.4 不真實的競爭程度
12.3.5 無用代碼的消除
12.4 其餘測試方式
12.4.1 代碼審查
12.4.2 靜態分析工具
findbugs的一些檢查模塊:
- 不一致的同步
- 調用Thread.run
- 未被釋放的鎖
- 空的同步塊
- 雙重檢查加鎖
- 在構造函數中啓動一個線程
- 通知錯誤
- 條件等待中的錯誤
- 對Lock和Condition的誤用
- 在休眠或者等待的同時持有一個鎖
- 自旋循環
12.4.3 面向切面的測試技術
12.4.4 分析與檢測工具
13.顯式鎖
13.1 Lock與ReentrantLock
Lock提供了更靈活的加鎖機制,
- void lock()
- void lockInterruptibly()
- boolean tryLock()
- boolean tryLock(long timeout,TimeUnit unit)
- void unlock()
- Condition newCondition();
13.1.1 輪詢鎖與定時鎖
經過tryLock()實現
可中斷的鎖獲取操做
lockInterruptibly()在中斷的同時保持對中斷的響應
13.1.3 非塊結構的加鎖
鎖分解,鎖分段
13.2 性能考慮因素
性能不是一成不變的,受cpu,處理器數量,緩存大小以及jvm特性等的影響
13.3 公平性
- 大部分時間,公平性的鎖性能要小於非公平性的鎖
- 緣由在於,線程喚醒不能當即運行,會形成鎖獲取的延遲和堵塞
13.4 在synchronized和ReentrantLock之間的進行選擇
通常默認選擇synchronized,除非用你要使用ReentranceLock的新特性
13.5 讀-寫鎖
- 讀寫鎖可使多個線程併發的訪問被保護對象,以讀取操做爲主時,能夠提升程序的可伸縮性
- 獲取寫入鎖時,其餘線程沒法獲取讀取鎖
- 寫入鎖線程能夠降級,但讀取鎖線程沒法升級;
14 構建自定義的同步工具
14.1 狀態依賴性的管理
14.1.1 實例:將前提條件的失敗傳遞給調用者
14.1.2 示例:經過輪詢與休眠來實現簡單的堵塞
14.1.3 條件隊列
14.2 使用條件隊列
14.2.1 條件謂詞
- 條件謂詞是使某個操做成爲狀態依賴操做的前提條件
- 將在條件隊列相關聯的條件謂詞以及相關操做寫入文檔
14.2.2 過早喚醒
notify,notifyAll,沒法判斷是哪一個線程發出的喚醒標誌;當使用條件等待時(Object.wait或Condition.await)
- 一般有一個條件謂詞-包括對一些對象狀態的測試,線程在執行前必須首先經過這些測試
- 在調用wait前測試條件謂詞,而且從wait中返回時再次進行測試
- 在一個循環中使用wait
- 確保使用與條件隊列相關的鎖來保護構成條件謂詞的各個狀態變量
- 當調用wait,notify或notifyAll等方法時,必定要持有與條件隊列相關的鎖
- 在檢查條件謂語以後以及開始執行相應的操做以前,不要釋放鎖
14.2.3 丟失的信號
信號可能會丟失,處理方案同14.2.2便可;
14.2.4 通知
- 每當在等待一個條件時,必定要確保在條件謂詞變爲真時經過某種方式發出通知.
- 應該優先使用notifyAll()
只有知足如下條件,才使用notify();
- 全部等待線程的類型都相同; 只有一個條件謂詞和相關條件隊列,每一個線程從wait返回後執行相同的操做
- 單進單出
單次通知和條件通知都屬於優化措施;
14.2.5 示例:閥門類
在條件變化時,必須發起通知;
14.2.6 子類的安全問題
- 若是違背了條件通知或者單次通知的某個需求,那麼在子類中能夠增長合適的通知機制來表明基類
- 等待和通知等協議徹底向子類公開;公開條件隊列和鎖,狀態變量,將條件謂詞和同步策略都寫入文檔
- 徹底禁止子類化;
14.2.7 封裝條件隊列
有時條件隊列也是鎖的一種
14.2.8 入口協議和出口協議
- 入口協議:該操做的條件謂詞
- 出口協議:檢查該操做修改的全部狀態變量,並確認他們是否使某個其餘的條件謂詞變成真,若是是,則通知相關的條件隊列
14.3 顯示的Condition對象
condition是一種廣義的內置條件隊列
- void await()
- boolean await(long time,TimeUnit unit)
- long awaitNanos(long nanosTimeout)
- void awaitUninterruptibly()
- boolean awaitUntil(date deadline)
- void signal()
- void signalAll()
Condition的功能:
14.4 Synchronizer剖析
都是基於AQS構建的
14.5 AbstractQueuedSynchronizer
-
getState,setState,compareAndSetState
-
acquire,release,isHeldExclusively
-
acquireShared,relseShared,
14.6 java.util.concurrnt同步器類中的AQS
14.6.1 ReentrantLock
tryAcquire爲例:
- 初始化,1.持有者owner;2.state(也是被持有的數量)爲0;
- 判斷:state==0,先嚐試用cas更新state,成功則設置owner,返回true
- status!=0,則判斷當前線程==ownner?state++:return false;
14.6.2 Semaphore與CountDownLatch
114.6.3 FutureTask
14.6.4 ReentrantReadWriteLock
15 原子變量和非阻塞同步機制
原子類不只支持可見性,還支持原子性操做;
15.1 鎖的劣勢
- 競爭激烈,調度消耗會很是大,調度開銷和工做開銷的比例會很是高
- 若是持有鎖的線程,有活躍性問題(死鎖,活鎖,無限等待),那麼全部鎖相關的線程都將阻塞
15.2 硬件對併發的支持
處理器直接支持,好比原子的測試並設置,比較並遞增,比較並交換
15.2.1 比較並交換
- 首先從V中讀取值A,並根據A計算新值B
- 經過cas以原子的方式直接將V中值由A變成B;
- 若是有競爭,其餘線程競爭失敗,則告知他們.並讓他們決定是否重試
15.2.2 非阻塞的計數器
-
使用鎖,須要jvm去遍歷代碼路徑;甚至操做系統級的鎖定,線程掛起,切換上下文
-
使用cas,能省去這步驟
-
缺點:須要調用者處理競爭問題(經過重試,回退,放棄),在鎖中能自動處理;
15.2.3 JVM對cas的支持
提供了不少類型的原子類型支持;
15.3 原子變量類
- 四組: 標量類,更新器類,數組類,複合變量類
- 標量類:AtomicLong,AtomicInteger,AtomicBoolean,AtomicReference
- 原子數組類(只支持Integer,Long,Reference),對立面的元素可見
- 原子標量類不該該用作散列容器中的key
15.3.1 原子變量是一種"更好的volatile"
15.3.2 性能比較:鎖與原子變量
- 在中低強度的競爭下,原子變量提供更高的可伸縮性
- 在高強度的競爭下,鎖可以更有效的避免競爭;
15.4 非堵塞算法
- 非阻塞算法;在某種算法中,一個線程的失敗或掛起不會致使其餘線程也失敗或掛起;技巧在於,將原子修改的範圍縮小到單個變量上
- 無鎖算法(lock-free):算法的每一個步驟都存在在每一個線程可以執行下去;
- 非阻塞算法不會出現死鎖和優先級反轉的問題
15.4.1 非阻塞的棧
- top做爲頭元素,其爲原子類
- push,pop都將會更新top,若是不成功,則重試;
15.4.2 非阻塞的鏈表
-
dummy節點,初始時做爲頭節點head和尾節點tail
-
循環:1.判斷tail.next!=null,則將tail使用cas設置爲tail.next;這是一個清理工做;
-
2.判斷tail.next==null,則cas(tail.next)成功,則cas(tail)返回true
-
3.即便2中cas(tail)錯誤,但下次put操做,會執行1步驟,會成功更新tail;併發中快速實現更新;
15.4.3 原子的域更新器
- AtomicReferenceFieldUpdater;
- 執行原子更新的同時還須要維持現有類的串行化形式那麼原子的更新器將很是有用
15.4.4 ABA問題
- AtomicStampedReference,AtomicMarkableReference
- 在引用上加版本號,從而避免ABA問題.
16 java內存模型
16.1 什麼是內存模型,爲何須要它
16.1.1 平臺的內存模型
jvm須要在內存插入內存柵欄屏蔽在JMM與底層平臺內存模型之間的差別,才能獲得該變量正確的值
16.1.2 重排序
16.1.3 java內存模型簡介
Happens-Before的規則(偏序關係):
- 程序順序規則;
- 監視器鎖規則;獲取以前必須限釋放
- volatile變量規則;寫入在讀取以前
- 線程啓動規則; 線程先啓動,裏面的代碼才能執行
- 線程結束規則;對線程的任何操做,要在線程結束以前完成;
- 中斷規則
- 終結器規則,構造函數在終結器以前執行完成.
- 傳遞性, A>B,B>C,A>C
16.1.4 藉助同步
各類同步工具都借用了Happer_before的原則;
- 隊列
- semaphore ,acquire,release()
- countDownLatch ,await,countDown()
- futureTask
- executor.execute
- cyclicBarrier
16.2 發佈
16.2.1 不安全的發佈
16.2.2 安全的發佈
16.2.3 安全初始化模式
16.2.4 雙重檢查加鎖
DCL已被拋棄,無競爭獲取鎖時速度已很是快
16.3 安全初始化
初始化安全性只能保證final域可達到的值從構造過程完成時開始的可見性.其餘的非final域可達的值,不能保證;