摘要:前言學習情況記錄時間子目標多線程記錄在學習線程安全知識點中,關于的有關知識點。對于資源競爭嚴重線程沖突嚴重的情況,自旋的概率會比較大,從而浪費更多的資源,效率低于。
前言
學習情況記錄
時間:week 1
SMART子目標 :Java 多線程
記錄在學習線程安全知識點中,關于CAS的有關知識點。
線程安全是指:多個線程不管以何種方式訪問某個類,并且在主調代碼中不需要進行同步,都能表現正確的行為。
常見的線程安全實現方法分為不可變對象、線程互斥同步、非阻塞同步、線程本地存儲等方案,本文要講的就是非阻塞同步中的核心CAS.
非阻塞同步從處理問題的方式上說,互斥同步屬于一種悲觀的并發策略。
隨著硬件指令集的發展,我們可以采用基于沖突檢查的樂觀并發策略,通俗地說,就是先行操作,如果沒有其他線程爭用共享數據,那操作就成功了;如果共享數據有爭用,產生了沖突,那就再采取其他的補償措施(最常見的補償措施就是不斷地重試,直到成功為止),這種樂觀的并發策略的許多實現偶讀不需要把線程掛起,因此這種同步操作稱為非阻塞同步。
CAS樂觀鎖需要操作和沖突檢測這兩個步驟具備原子性,這里就不能再使用互斥同步來保證了,只能靠硬件來完成。硬件支持的原子性操作最典型的是:比較并交換(Compare-and-Swap,CAS)。CAS 指令需要有 3 個操作數,分別是內存地址 V、舊的預期值 A 和新值 B。當執行操作時,只有當 V 的值等于 A,才將 V 的值更新為 B。
各種Atomic開頭的原子類,內部都應用到了CAS。就拿AtomicInteger為例。
J.U.C 包里面的原子類 AtomicInteger 的方法調用了 Unsafe 類的 CAS 操作。
看看AtomicInteger對象一次自增,CAS起了什么作用,以下代碼是 incrementAndGet() 的源碼,可以看到內部調用了 Unsafe 對象的 getAndAddInt() 。
以下代碼是 getAndAddInt()源碼,var1 指示對象內存地址,var2指示該字段相對對象內存地址的偏移,var4 指示操作需要加的數值,這里為 1。通過 getIntVolatile(var1, var2) 得到舊的預期值,通過調用 compareAndSwapInt() 來進行 CAS 比較,如果該字段內存地址中的值等于 var5,那么就更新內存地址為 var1+var2 的變量為 var5+var4。
compareAndSwapInt(var1, var2, var5, var5 + var4 其實換成compareAndSwapInt(obj, offset, expect, update)比較清楚,意思就是如果obj內的value和expect相等,就證明沒有其他線程改變過這個變量,那么就更新它為update,如果這一步的CAS沒有成功,那就采用自旋的方式繼續進行CAS操作,取出乍一看這也是兩個步驟了啊,其實在JNI里是借助于一個CPU指令完成的。所以還是原子操作。
CAS 的問題
ABA問題
描述:如果一個變量初次讀取的時候是 A 值,它的值被改成了 B,后來又被改回為 A,那 CAS 操作就會誤認為它從來沒有被改變過。
解決方案:J.U.C 包提供了一個帶有標記的原子引用類 AtomicStampedReference 來解決這個問題,它可以通過控制變量值的版本來保證 CAS 的正確性。大部分情況下 ABA 問題不會影響程序并發的正確性,如果真的需要解決 ABA 問題,改用傳統的互斥同步可能會比原子類更高效。
循環時間長開銷大
自旋CAS(也就是不成功就一直循環執行直到成功)如果長時間不成功,會給CPU帶來比較大的執行開銷。
只能保證一個共享變量的原子操作
CAS 只對單個共享變量有效,當操作涉及跨多個共享變量時CAS 無效。但是從 JDK 1.5開始,提供了AtomicReference類來保證引用對象之間的原子性,你可以把多個變量放在一個對象里來進行 CAS 操作.所以我們可以使用鎖或者利用AtomicReference類把多個共享變量合并成一個共享變量來操作。
CAS與synchronized的使用情景簡單的來說CAS適用于寫比較少的情況下(多讀場景,沖突一般較少)
synchronized適用于寫比較多的情況下(多寫場景,沖突一般較多)
對于資源競爭較少(線程沖突較輕)的情況,使用synchronized同步鎖進行線程阻塞和喚醒切換以及用戶態內核態間的切換操作額外浪費消耗cpu資源;而CAS基于硬件實現,不需要進入內核,不需要切換線程,操作自旋幾率較少,因此可以獲得更高的性能。
對于資源競爭嚴重(線程沖突嚴重)的情況,CAS自旋的概率會比較大,從而浪費更多的CPU資源,效率低于synchronized。
CAS 的應用使用 CAS 原子指令來處理對數據的并發訪問,這是非阻塞算法得以實現的基礎。關于非阻塞算法是屬于J.U.C中并發容器部分的知識,屬于比較難的內容。目前先引用幾篇文章。作為記錄,之后有機會再詳細學習。
非阻塞算法在并發容器中的實現
非阻塞同步算法實戰(一)
非阻塞同步算法實戰(二)-BoundlessCyclicBarrier
非阻塞同步算法實戰(三)-LatestResultsProvider
參考《深入理解Java虛擬機》
https://www.ibm.com/developer...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75197.html
摘要:方法由兩個參數,表示期望的值,表示要給設置的新值。操作包含三個操作數內存位置預期原值和新值。如果處的值尚未同時更改,則操作成功。中就使用了這樣的操作。上面操作還有一點是將事務范圍縮小了,也提升了系統并發處理的性能。 這是java高并發系列第21篇文章。 本文主要內容 從網站計數器實現中一步步引出CAS操作 介紹java中的CAS及CAS可能存在的問題 悲觀鎖和樂觀鎖的一些介紹及數據庫...
摘要:算法算法會先對一個內存變量位置和一個給定的值進行比較,如果相等,則用一個新值去修改這個內存變量位置。因為是非公平鎖,所以一上來就嘗試搶占鎖給定舊值并希望用新值去更新內存變量。 本文翻譯和原創各占一半,所以還是厚顏無恥歸類到原創好了...https://howtodoinjava.com/jav...java 5 其中一個令人振奮的改進是新增了支持原子操作的類型,例如 AtomicInt...
摘要:前言在上一篇文章中多線程奇幻之旅算法實現線程安全,我們介紹了和方式實現線程安全類的方法,兩種方式一個是鎖定阻塞方式,一個是非阻塞方式。 前言 在上一篇文章中《Java多線程奇幻之旅——CAS算法實現線程安全》,我們介紹了Synchronized和CAS方式實現線程安全類的方法,兩種方式一個是鎖定阻塞方式,一個是非阻塞方式。本文專注于兩種實現方式效率問題。本文是上篇文章的延續,會借用到上...
摘要:這個規則比較好理解,無論是在單線程環境還是多線程環境,一個鎖處于被鎖定狀態,那么必須先執行操作后面才能進行操作。線程啟動規則獨享的方法先行于此線程的每一個動作。 1. 指令重排序 關于指令重排序的概念,比較復雜,不好理解。我們從一個例子分析: public class SimpleHappenBefore { /** 這是一個驗證結果的變量 */ private st...
閱讀 3735·2021-11-24 10:46
閱讀 1706·2021-11-15 11:38
閱讀 3761·2021-11-15 11:37
閱讀 3481·2021-10-27 14:19
閱讀 1939·2021-09-03 10:36
閱讀 1991·2021-08-16 11:02
閱讀 2998·2019-08-30 15:55
閱讀 2251·2019-08-30 15:44