日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JUC多线程:Atomic原子类与CAS原理

發(fā)布時間:2024/9/30 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JUC多线程:Atomic原子类与CAS原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、Atomic 原子類的原理:

????????Atomic 原子操作類是基于無鎖 CAS + volatile 實現(xiàn)的,并且類中的所有方法都使用 final 修飾,進一步保證線程安全。而 CAS 算法的具體實現(xiàn)方式在于 Unsafe 類中,Unsafe 類的所有方法都是 native 修飾的,也就是說所有方法都是直接調(diào)用操作系統(tǒng)底層資源進行執(zhí)行相應(yīng)任務(wù)。Atomic 使用樂觀策略,每次操作時都假設(shè)沒有沖突發(fā)生,并采用 volatile 配合 CAS 去修改內(nèi)存中的變量,如果失敗則重試,直到成功為止。

  • 樂觀鎖:樂觀鎖認為競爭不總是發(fā)生,因此每次操作共享資源時都不需要加鎖,并將“比較-替換”這兩個動作作為一個原子操作去修改內(nèi)存中的變量,一旦發(fā)生沖突就重試,直到成功為止。無鎖策略就是一種樂觀策略,采用 volatile + CAS 來保證線程執(zhí)行的安全性。

  • 悲觀鎖:悲觀鎖認為每次訪問共享資源總會發(fā)生沖突,因此每次對共享資源進行操作時,都會去事先申請一個獨占的鎖,比如 synchronized 和 ReentronLock 都是獨占鎖。

二、什么是 CAS:Compare And Swap:

1、CAS 的算法核心思想:

執(zhí)行函數(shù):CAS(V,E,U),其包含3個參數(shù):內(nèi)存值V,舊預(yù)期值E,要修改的值U。

  • ① 當(dāng)且僅當(dāng) 預(yù)期值E 和 內(nèi)存值V 相同時,才會將內(nèi)存值修改為U并返回true;

  • ② 若V值和E值不同,則說明已經(jīng)有其他線程做了更新,則當(dāng)前線程不執(zhí)行更新操作,但可以選擇重新讀取該變量再嘗試再次修改該變量,也可以放棄操作。

CAS 一定要 volatile 變量配合,這樣才能保證每次拿到的變量是主內(nèi)存中最新的那個值,否則舊的預(yù)期值E對某條線程來說,永遠是一個不會變的值E,只要某次CAS操作失敗,永遠都不可能成功。由于 CAS 無鎖操作中沒有鎖的存在,因此不可能出現(xiàn)死鎖的情況,也就是天生免疫死鎖。

2、CPU 指令對 CAS 的支持:

????????由于 CAS 的步驟很多,那會不會存在一種情況:假設(shè)某個線程在判斷 V 和 E 相同后,正要賦值時,切換了線程,更改了值,從而造成了數(shù)據(jù)不一致呢?答案是否定的,因為 CAS 是一種系統(tǒng)原語,原語屬于操作系統(tǒng)用語范疇,是由若干條指令組成的,用于完成某個功能的一個過程,并且原語的執(zhí)行必須是連續(xù)的,在執(zhí)行過程中不允許被中斷,也就是說CAS是一條CPU的原子指令,不會造成所謂的數(shù)據(jù)不一致問題。

3、CAS 的 ABA 問題及其解決方案:

假設(shè)這樣一種場景,當(dāng)?shù)谝粋€線程執(zhí)行 CAS(V,E,U) 操作,在獲取到當(dāng)前變量V,準(zhǔn)備修改為新值U前,另外兩個線程已連續(xù)修改了兩次變量V的值,使得該值又恢復(fù)為舊值,這樣的話,我們就無法正確判斷這個變量是否已被修改過。

解決方法:使用帶版本的標(biāo)志或者時間戳解決ABA問題,在更新數(shù)據(jù)時,只有要更新的數(shù)據(jù)和版本標(biāo)識符合期望值,才允許替換。

?

三、Unsafe 類:

????????Atomic 中 CAS 操作的執(zhí)行依賴于 Unsafe 類的方法,Unsafe 類中的所有方法都是 native 修飾的,也就是說所有方法都直接調(diào)用操作系統(tǒng)底層資源執(zhí)行相應(yīng)任務(wù)。Unsafe類提供了很多功能,這里我們主要介紹 Unsafe 的 CAS,對其他功能感興趣的讀者可以去閱讀這篇文章:https://blog.csdn.net/javazejian/article/details/72772470

Unsafe 類存在于 sun.misc 包中,其內(nèi)部方法操作可以像C的指針一樣直接操作內(nèi)存,單從名稱看來就可以知道該類是非安全的,因為 Unsafe 擁有著類似于C的指針操作,因此總是不應(yīng)該首先使用 Unsafe 類,Java 官方也不建議直接使用的 Unsafe 類

無鎖操作 CAS 是一些CPU直接支持的指令,在 Java 中無鎖操作 CAS 基于以下3個方法實現(xiàn),在稍后講解Atomic系列內(nèi)部方法就是基于下述方法的實現(xiàn)的。

//第一個參數(shù)o為給定對象,offset為對象內(nèi)存的偏移量,通過這個偏移量迅速定位字段并設(shè)置或獲取該字段的值, //expected表示期望值,x表示要設(shè)置的值,下面3個方法都通過CAS原子指令執(zhí)行操作。 public final native boolean compareAndSwapObject(Object o, long offset,Object expected, Object x); public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);public final native boolean compareAndSwapLong(Object o, long offset,long expected,long x);

同時 Unsafe 類中在 JDK8 新增的幾個方法,它們的實現(xiàn)是基于上述的CAS方法,如下:

//1.8新增,給定對象o,根據(jù)獲取內(nèi)存偏移量指向的字段,將其增加delta,//這是一個CAS操作過程,直到設(shè)置成功方能退出循環(huán),返回舊值public final int getAndAddInt(Object o, long offset, int delta) {int v;do {//獲取內(nèi)存中最新值v = getIntVolatile(o, offset);//通過CAS操作} while (!compareAndSwapInt(o, offset, v, v + delta));return v;}//1.8新增,方法作用同上,只不過這里操作的long類型數(shù)據(jù)public final long getAndAddLong(Object o, long offset, long delta) {long v;do {v = getLongVolatile(o, offset);} while (!compareAndSwapLong(o, offset, v, v + delta));return v;}//1.8新增,給定對象o,根據(jù)獲取內(nèi)存偏移量對于字段,將其 設(shè)置為新值newValue,//這是一個CAS操作過程,直到設(shè)置成功方能退出循環(huán),返回舊值public final int getAndSetInt(Object o, long offset, int newValue) {int v;do {v = getIntVolatile(o, offset);} while (!compareAndSwapInt(o, offset, v, newValue));return v;}// 1.8新增,同上,操作的是long類型public final long getAndSetLong(Object o, long offset, long newValue) {long v;do {v = getLongVolatile(o, offset);} while (!compareAndSwapLong(o, offset, v, newValue));return v;}//1.8新增,同上,操作的是引用類型數(shù)據(jù)public final Object getAndSetObject(Object o, long offset, Object newValue) {Object v;do {v = getObjectVolatile(o, offset);} while (!compareAndSwapObject(o, offset, v, newValue));return v;}

四、原子操作類?Atomic:

原子更新基本類型主要包括3個類:

  • AtomicBoolean:原子更新布爾類型

  • AtomicInteger:原子更新整型

  • AtomicLong:原子更新長整型

這3個類的實現(xiàn)原理和使用方式幾乎是一樣的,這里我們以 AtomicInteger 為例進行分析,AtomicInteger 主要是針對 int 類型的數(shù)據(jù)執(zhí)行原子操作,它提供了原子自增方法、原子自減方法以及原子賦值方法等,鑒于AtomicInteger的源碼不多,我們直接看源碼:

public class AtomicInteger extends Number implements java.io.Serializable {private static final long serialVersionUID = 6214790243416807050L;// 獲取指針類Unsafeprivate static final Unsafe unsafe = Unsafe.getUnsafe();//下述變量value在AtomicInteger實例對象內(nèi)的內(nèi)存偏移量private static final long valueOffset;static {try {//通過unsafe類的objectFieldOffset()方法,獲取value變量在對象內(nèi)存中的偏移//通過該偏移量valueOffset,unsafe類的內(nèi)部方法可以獲取到變量value對其進行取值或賦值操作valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}//當(dāng)前AtomicInteger封裝的int變量valueprivate volatile int value;public AtomicInteger(int initialValue) {value = initialValue;}public AtomicInteger() {}//獲取當(dāng)前最新值,public final int get() {return value;}//設(shè)置當(dāng)前值,具備volatile效果,方法用final修飾是為了更進一步的保證線程安全。public final void set(int newValue) {value = newValue;}//最終會設(shè)置成newValue,使用該方法后可能導(dǎo)致其他線程在之后的一小段時間內(nèi)可以獲取到舊值,有點類似于延遲加載public final void lazySet(int newValue) {unsafe.putOrderedInt(this, valueOffset, newValue);}//設(shè)置新值并獲取舊值,底層調(diào)用的是CAS操作即unsafe.compareAndSwapInt()方法public final int getAndSet(int newValue) {return unsafe.getAndSetInt(this, valueOffset, newValue);}//如果當(dāng)前值為expect,則設(shè)置為update(當(dāng)前值指的是value變量)public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}//當(dāng)前值加1返回舊值,底層CAS操作public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);}//當(dāng)前值減1,返回舊值,底層CAS操作public final int getAndDecrement() {return unsafe.getAndAddInt(this, valueOffset, -1);}//當(dāng)前值增加delta,返回舊值,底層CAS操作public final int getAndAdd(int delta) {return unsafe.getAndAddInt(this, valueOffset, delta);}//當(dāng)前值加1,返回新值,底層CAS操作public final int incrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, 1) + 1;}//當(dāng)前值減1,返回新值,底層CAS操作public final int decrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, -1) - 1;}//當(dāng)前值增加delta,返回新值,底層CAS操作public final int addAndGet(int delta) {return unsafe.getAndAddInt(this, valueOffset, delta) + delta;}//省略一些不常用的方法.... }

可以發(fā)現(xiàn) AtomicInteger 原子類的內(nèi)部幾乎是基于 Unsafe 類中的 CAS 相關(guān)操作的方法實現(xiàn)的,這也同時證明 AtomicInteger 是基于無鎖實現(xiàn)的,這里重點分析自增操作實現(xiàn)過程,其他方法自增實現(xiàn)原理一樣。

//當(dāng)前值加1,返回新值,底層CAS操作 public final int incrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, 1) + 1;}

我們發(fā)現(xiàn) AtomicInteger 類中所有自增或自減的方法都間接調(diào)用 Unsafe 類中的 getAndAddInt() 方法實現(xiàn)了CAS操作,從而保證了線程安全,關(guān)于 getAndAddInt() 其實前面已分析過,它是 Unsafe 類中1.8新增的方法,源碼如下:

//Unsafe類中的getAndAddInt方法 public final int getAndAddInt(Object o, long offset, int delta) {int v;do {v = getIntVolatile(o, offset);} while (!compareAndSwapInt(o, offset, v, v + delta));return v;}

可看出 getAndAddInt() 通過一個 while 循環(huán)不斷的重試更新要設(shè)置的值,直到成功為止,調(diào)用的是 Unsafe 類中的 compareAndSwapInt() 方法,是一個 CAS 操作方法。這里需要注意的是,上述源碼分析是基于JDK1.8的,如果是1.8之前的方法,AtomicInteger 源碼實現(xiàn)有所不同,是基于 for 死循環(huán)的,如下:

//JDK 1.7的源碼,由for的死循環(huán)實現(xiàn),并且直接在AtomicInteger實現(xiàn)該方法,JDK1.8后,該方法實現(xiàn)已移動到Unsafe類中,直接調(diào)用getAndAddInt方法即可 public final int incrementAndGet() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return next;} }

總結(jié)

以上是生活随笔為你收集整理的JUC多线程:Atomic原子类与CAS原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。