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