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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

面试必问的 CAS ,要多了解

發布時間:2023/12/3 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 面试必问的 CAS ,要多了解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載自?面試必問的 CAS ,要多了解

前言

CAS(Compare and Swap),即比較并替換,實現并發算法時常用到的一種技術,Doug lea大神在java同步器中大量使用了CAS技術,鬼斧神工的實現了多線程執行的安全性。

CAS的思想很簡單:三個參數,一個當前內存值V、舊的預期值A、即將更新的值B,當且僅當預期值A和內存值V相同時,將內存值修改為B并返回true,否則什么都不做,并返回false。

問題

一個n++的問題。

12345678publicclass Case {????publicvolatile int n;????publicvoid add() {????????n++;????}}

通過javap -verbose Case看看add方法的字節碼指令

1234567891011publicvoid add();????flags: ACC_PUBLIC????Code:??????stack=3, locals=1, args_size=1?????????0: aload_0?????? ?????????1: dup?????????? ?????????2: getfield????? #2?????????????????// Field n:I?????????5: iconst_1????? ?????????6: iadd????????? ?????????7: putfield????? #2?????????????????// Field n:I????????10:return

n++被拆分成了幾個指令:

  • 執行getfield拿到原始n;
  • 執行iadd進行加1操作;
  • 執行putfield寫把累加后的值寫回n;
  • 通過volatile修飾的變量可以保證線程之間的可見性,但并不能保證這3個指令的原子執行,在多線程并發執行下,無法做到線程安全,得到正確的結果,那么應該如何解決呢?

    如何解決

    在add方法加上synchronized修飾解決。

    12345678publicclass Case {????publicvolatile int n;????publicsynchronized void add() {????????n++;????}}

    這個方案當然可行,但是性能上差了點,還有其它方案么?

    再來看一段代碼

    12345678publicint a = 1;publicboolean compareAndSwapInt(intb) {????if(a == 1) {????????a = b;????????returntrue;????}????returnfalse;}

    如果這段代碼在并發下執行,會發生什么?

    假設線程1和線程2都過了a==1的檢測,都準備執行對a進行賦值,結果就是兩個線程同時修改了變量a,顯然這種結果是無法符合預期的,無法確定a的最終值。

    解決方法也同樣暴力,在compareAndSwapInt方法加鎖同步,變成一個原子操作,同一時刻只有一個線程才能修改變量a。

    除了低性能的加鎖方案,我們還可以使用JDK自帶的CAS方案,在CAS中,比較和替換是一組原子操作,不會被外部打斷,且在性能上更占有優勢。

    下面以AtomicInteger的實現為例,分析一下CAS是如何實現的。

    123456789101112131415publicclass AtomicInteger extendsNumber implementsjava.io.Serializable {????// setup to use Unsafe.compareAndSwapInt for updates????privatestatic final Unsafe unsafe = Unsafe.getUnsafe();????privatestatic final long valueOffset;????static{????????try{????????????valueOffset = unsafe.objectFieldOffset????????????????(AtomicInteger.class.getDeclaredField("value"));????????}catch(Exception ex) { thrownew Error(ex); }????}????privatevolatile int value;????publicfinal int get() {returnvalue;}}
  • Unsafe,是CAS的核心類,由于Java方法無法直接訪問底層系統,需要通過本地(native)方法來訪問,Unsafe相當于一個后門,基于該類可以直接操作特定內存的數據。
  • 變量valueOffset,表示該變量值在內存中的偏移地址,因為Unsafe就是根據內存偏移地址獲取數據的。
  • 變量value用volatile修飾,保證了多線程之間的內存可見性。
  • 看看AtomicInteger如何實現并發下的累加操作:

    123456789101112publicfinal int getAndAdd(intdelta) {??? ????returnunsafe.getAndAddInt(this, valueOffset, delta);}//unsafe.getAndAddIntpublicfinal int getAndAddInt(Object var1, longvar2, intvar4) {????intvar5;????do{????????var5 = this.getIntVolatile(var1, var2);????}while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));????returnvar5;}

    假設線程A和線程B同時執行getAndAdd操作(分別跑在不同CPU上):

  • AtomicInteger里面的value原始值為3,即主內存中AtomicInteger的value為3,根據Java內存模型,線程A和線程B各自持有一份value的副本,值為3。
  • 線程A通過getIntVolatile(var1, var2)拿到value值3,這時線程A被掛起。
  • 線程B也通過getIntVolatile(var1, var2)方法獲取到value值3,運氣好,線程B沒有被掛起,并執行compareAndSwapInt方法比較內存值也為3,成功修改內存值為2。
  • 這時線程A恢復,執行compareAndSwapInt方法比較,發現自己手里的值(3)和內存的值(2)不一致,說明該值已經被其它線程提前修改過了,那只能重新來一遍了。
  • 重新獲取value值,因為變量value被volatile修飾,所以其它線程對它的修改,線程A總是能夠看到,線程A繼續執行compareAndSwapInt進行比較替換,直到成功。
  • 整個過程中,利用CAS保證了對于value的修改的并發安全,繼續深入看看Unsafe類中的compareAndSwapInt方法實現。

    1publicfinal native boolean compareAndSwapInt(Object paramObject, longparamLong, intparamInt1, intparamInt2);

    Unsafe類中的compareAndSwapInt,是一個本地方法,該方法的實現位于unsafe.cpp中

    123456UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))??UnsafeWrapper("Unsafe_CompareAndSwapInt");??oop p = JNIHandles::resolve(obj);??jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);??return(jint)(Atomic::cmpxchg(x, addr, e)) == e;UNSAFE_END
  • 先想辦法拿到變量value在內存中的地址。
  • 通過Atomic::cmpxchg實現比較替換,其中參數x是即將更新的值,參數e是原內存的值。
  • 如果是Linux的x86,Atomic::cmpxchg方法的實現如下:

    12345678inline jint Atomic::cmpxchg (jint exchange_value, volatilejint* dest, jint compare_value) {??intmp = os::is_MP();??__asm__volatile(LOCK_IF_MP(%4)"cmpxchgl %1,(%3)"????????????????????:"=a"(exchange_value)????????????????????:"r"(exchange_value), "a"(compare_value), "r"(dest), "r"(mp)????????????????????:"cc","memory");??returnexchange_value;}

    看到這匯編,內心崩潰

    __asm__表示匯編的開始
    volatile表示禁止編譯器優化
    LOCK_IF_MP是個內聯函數

    1#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "

    Window的x86實現如下:

    12345678910111213141516171819inline jint Atomic::cmpxchg (jint exchange_value, volatilejint* dest, jint compare_value) {????intmp = os::isMP(); //判斷是否是多處理器????_asm {????????mov edx, dest????????mov ecx, exchange_value????????mov eax, compare_value????????LOCK_IF_MP(mp)????????cmpxchg dword ptr [edx], ecx????}}// Adding a lock prefix to an instruction on MP machine// VC++ doesn't like the lock prefix to be on a single line// so we can't insert a label after the lock prefix.// By emitting a lock prefix, we can define a label after it.#define LOCK_IF_MP(mp) __asm cmp mp, 0?\???????????????????????__asm je L0????? \???????????????????????__asm _emit 0xF0\???????????????????????__asm L0:

    LOCK_IF_MP根據當前系統是否為多核處理器決定是否為cmpxchg指令添加lock前綴。

  • 如果是多處理器,為cmpxchg指令添加lock前綴。
  • 反之,就省略lock前綴。(單處理器會不需要lock前綴提供的內存屏障效果)
  • intel手冊對lock前綴的說明如下:

  • 確保后續指令執行的原子性。
  • 在Pentium及之前的處理器中,帶有lock前綴的指令在執行期間會鎖住總線,使得其它處理器暫時無法通過總線訪問內存,很顯然,這個開銷很大。在新的處理器中,Intel使用緩存鎖定來保證指令執行的原子性,緩存鎖定將大大降低lock前綴指令的執行開銷。
  • 禁止該指令與前面和后面的讀寫指令重排序。
  • 把寫緩沖區的所有數據刷新到內存中。
  • 上面的第2點和第3點所具有的內存屏障效果,保證了CAS同時具有volatile讀和volatile寫的內存語義。

    CAS缺點

    CAS存在一個很明顯的問題,即ABA問題。

    問題:如果變量V初次讀取的時候是A,并且在準備賦值的時候檢查到它仍然是A,那能說明它的值沒有被其他線程修改過了嗎?

    如果在這段期間曾經被改成B,然后又改回A,那CAS操作就會誤認為它從來沒有被修改過。針對這種情況,java并發包中提供了一個帶有標記的原子引用類AtomicStampedReference,它可以通過控制變量值的版本來保證CAS的正確性。


    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的面试必问的 CAS ,要多了解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 嫩草影院永久入口 | 成人精品免费在线观看 | 嫩草国产精品 | 偷偷操不一样的久久 | 欧美tickle狂笑裸体vk | 亚洲无毛视频 | 国产婷婷色一区二区 | 四虎影视永久免费 | av五十路 | 成全影视在线观看第8季 | 精品国产乱码久久久久夜深人妻 | 欧美成性色| 五月天小说网 | 欧美日韩免费观看一区=区三区 | 日韩电影在线观看一区 | wwwxxoo| 噼里啪啦免费看 | 污污网站在线观看视频 | 免费中文字幕在线观看 | 精品在线视频免费 | 在线播放国产精品 | 国产欧美一区二区三区在线看 | 国产精品一区二区三区线羞羞网站 | 色综合色综合 | 琪琪色在线视频 | 国产精品日韩在线观看 | 日本少妇激情视频 | 超碰成人在线观看 | 日本孰妇毛茸茸xxxx | 操亚洲女人 | 日本中文字幕影院 | 亚洲午夜精品 | av每日更新 | 国产精品一区在线播放 | 男女av| 久草热线| 精品一区二区免费看 | 一级h片 | 思思在线视频 | 成人国产av一区二区三区 | 乌克兰做爰xxxⅹ性视频 | 久久久久久久国产精品视频 | 少妇粉嫩小泬白浆流出 | 18禁免费观看网站 | 白白色在线播放 | 丁香六月在线 | 日韩1级片 | av官网在线观看 | 欧美偷拍精品 | 91在线免费看片 | 特级西西444www高清大胆免费看 | 成人欧美一区二区三区在线观看 | 国产69精品麻豆 | 亚洲av午夜精品一区二区三区 | 亚洲中文字幕无码爆乳av | 国产亚洲精品久久久久丝瓜 | 中国美女一级片 | 女人的黄色片 | 九九热在线播放 | 四虎国产 | 国产精品一区麻豆 | 日韩精品激情 | 国产精品一线二线 | 精品免费囯产一区二区三区 | 国产精品国产三级国产在线观看 | 爽爽影院在线免费观看 | 久草视| 亚洲专区区免费 | 国产chinese男男网站大全 | 美女扒开下面让男人捅 | 手机在线看片日韩 | 国产午夜大地久久 | 亚洲午夜久久久久久久久 | 国产亚洲精久久久久久无码苍井空 | 国内外成人激情视频 | 裸体毛片 | 国产第一页第二页 | 国产中文欧美日韩在线 | 国产精品久久久久久久久久久久久久久 | 性欧美18一19性猛交 | 一区二区三区久久精品 | 欧美在线91 | 久久字幕 | 久久精品一区二区三区黑人印度 | 欧美亚洲专区 | 长篇高h肉爽文丝袜 | 欧美日韩一区二区区别是什么 | 亚洲图片在线观看 | 毛片91| 激情综合站 | 黄色一级片在线播放 | 久久天天躁狠狠躁夜夜av | 涩涩在线看 | 日韩欧美中文字幕在线视频 | 黄色高清在线观看 | 艳妇臀荡乳欲伦交换gif | 精品人妻一区二区三区浪潮在线 | 青青艹av | 美女扒开腿让男人 |