成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

CAS與ABA問題及解決方式

系統
要了解ABA問題,我們得先知道什么是CAS,CAS 全稱是 compare and swap,是一種用于在多線程環境下實現同步功能的機制。CAS的出現主要是為了解決多線程并發情況下,數據的不一致問題。

[[384754]]

 要了解ABA問題,我們得先知道什么是CAS,CAS 全稱是 compare and swap,是一種用于在多線程環境下實現同步功能的機制。CAS的出現主要是為了解決多線程并發情況下,數據的不一致問題。

CAS底層原理

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

Unsafe類

Unsafe類是CAS的核心類,由于Java方法無法直接訪問底層系統,需要通過本地(native)方法來訪問,基于該類可以直接操作特定內存的數據。Unsafe類存在與sum.misc包中,其內部實現是C++寫的,我從JDK1.8源碼中截取了關鍵代碼

  1. UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) 
  2.  UnsafeWrapper("Unsafe_CompareAndSwapInt"); 
  3.  oop p = JNIHandles::resolve(obj); 
  4.  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset); 
  5.  return (jint)(Atomic::cmpxchg(x, addr, e)) == e; 
  6. UNSAFE_END 

從上面代碼可以看出最后調用的是Atomic:comxchg這個方法,這個方法的實現放在hotspot下的os_cpu包中,說明這個方法的實現和操作系統、CPU都有關系,以多核CPU為例:

  • 首先會判斷CPU是否為多核,如果是多核加一個lock內存屏障,這樣就可以防止多線程并發情況競爭發生
  • 進行對比交換,調用匯編指令cmpxchg獲取新值并設值。

CAS問題

cas實現

從JDK1.5開始,java.util.concurrent包為我們提供了許多cas操作類諸如:AtomicInteger,

AtomicLong,AtomicReference,它提供了輕量級的鎖機制有著更好的性能,但同時也會出現一些問題,我們通過一張圖來說明:


上圖運行過程中可能會出現兩個問題:

  • 線程3可能一直拿不到最新的值,導致線程自旋
  • 主內存有個數據值:A,兩個線程A和B分別copy主內存數據到自己的工作區,A執行比較慢,需要10秒, B執行比較快,需要2秒, 此時B線程將主內存中的數據更改為B,過了一會又更改為A,然后A線程執行比較,發現結果是A,以為別人沒有動過,然后執行更改操作。其實中間已經被更改過了,這就是ABA問題。

ABA問題的優化

ABA問題導致的原因,是CAS過程中只簡單進行了“值”的校驗,再有些情況下,“值”相同不會引入錯誤的業務邏輯(例如庫存),有些情況下,“值”雖然相同,卻已經不是原來的數據了。那如何能避免ABA問題呢?優化的方式也很簡單,就是不能只對值進行比較,通過對值打標簽的方式就能很好的避免ABA問題。JAVA中也為我們提供了相應的處理類AtomicStampReferenceAtomicStampReference在cas的基礎上增加了一個標記stamp,使用這個標記可以用來覺察數據是否發生變化,給數據帶上了一種實效性的檢驗。它有以下幾個參數:

  1. //參數代表的含義分別是 期望值,寫入的新值,期望標記,新標記值 
  2. public boolean compareAndSet(V expected,V newReference,int expectedStamp,int newStamp); 
  3.  
  4. public V getRerference(); 
  5.  
  6. public int getStamp(); 
  7.  
  8. public void set(V newReference,int newStamp); 

我們通過一個示例來說明:

  1. public class Test { 
  2.  
  3. private static AtomicReference<Integer> atomicReference = new AtomicReference<Integer>(100); 
  4.  
  5. public static void main(String[] args) { 
  6. new Thread(() -> { 
  7. atomicReference.compareAndSet(100, 101); 
  8. atomicReference.compareAndSet(101, 100); 
  9. },"t1").start(); 
  10.  
  11. new Thread(() -> { 
  12. try { 
  13. TimeUnit.SECONDS.sleep(1); 
  14. } catch (InterruptedException e) { 
  15. e.printStackTrace(); 
  16. System.out.println(atomicReference.compareAndSet(100, 2021) + "\t修改后的值:" + atomicReference.get()); 
  17. },"t2").start(); 
  • 初始值為100,線程t1將100改成101,然后又將101改回100
  • 線程t2先睡眠1秒,等待t1操作完成,然后t2線程將值改成2019

可以看到,線程2修改成功。輸出結果:

  1. true 修改后的值:2021 

要解決ABA問題,可以增加一個版本號,當內存位置V的值每次被修改后,版本號都加1AtomicStampedReference內部維護了對象值和版本號,在創建AtomicStampedReference對象時,需要傳入初始值和初始版本號, 當AtomicStampedReference設置對象值時,對象值以及狀態戳都必須滿足期望值,寫入才會成功

  1. public class Test { 
  2.  
  3.  
  4. private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(100,1); 
  5.  
  6. public static void main(String[] args) { 
  7. new Thread(() -> { 
  8. System.out.println("t1拿到的初始版本號:" + atomicStampedReference.getStamp()); 
  9.  
  10. //睡眠1秒,是為了讓t2線程也拿到同樣的初始版本號 
  11. try { 
  12. TimeUnit.SECONDS.sleep(1); 
  13. } catch (InterruptedException e) { 
  14. e.printStackTrace(); 
  15. atomicStampedReference.compareAndSet(100, 101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); 
  16. atomicStampedReference.compareAndSet(101, 100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); 
  17. },"t1").start(); 
  18.  
  19. new Thread(() -> { 
  20. int stamp = atomicStampedReference.getStamp(); 
  21. System.out.println("t2拿到的初始版本號:" + stamp); 
  22.  
  23. //睡眠3秒,是為了讓t1線程完成ABA操作 
  24. try { 
  25. TimeUnit.SECONDS.sleep(3); 
  26. } catch (InterruptedException e) { 
  27. e.printStackTrace(); 
  28. System.out.println("最新版本號:" + atomicStampedReference.getStamp()); 
  29. System.out.println(atomicStampedReference.compareAndSet(100, 2021,stamp,atomicStampedReference.getStamp() + 1) + "\t當前 值:" + atomicStampedReference.getReference()); 
  30. },"t2").start(); 
  • 初始值100,初始版本號1
  • 線程t1和t2拿到一樣的初始版本號
  • 線程t1完成ABA操作,版本號遞增到3
  • 線程t2完成CAS操作,最新版本號已經變成3,跟線程t2之前拿到的版本號1不相等,操作失敗

輸出結果:

  1. t1拿到的初始版本號:1 
  2. t2拿到的初始版本號:1 
  3. 最新版本號:3 
  4. false當前 值:100 

 【編輯推薦】

 

責任編輯:姜華 來源: 編碼是個技術活
相關推薦

2017-06-23 07:15:52

庫存ABACAS

2024-11-19 17:54:15

JavaCASABA問題

2022-11-16 21:55:51

Redis數據庫

2019-09-05 08:54:38

一致性CASABA

2016-11-29 09:00:19

分布式數據一致性CAS

2014-12-26 10:23:21

谷歌

2021-02-02 18:02:09

java對象數據

2021-02-08 21:07:47

JavaCAS機制

2010-10-08 16:31:08

AjaxIE6

2017-08-03 09:37:35

SparkStreamKafkaDirect

2012-08-08 14:33:32

IBMdW

2009-07-01 18:14:36

JSP亂碼

2013-03-20 09:54:07

2011-05-19 14:16:29

網頁設計

2013-04-24 17:05:15

2009-12-08 10:52:30

WCF雙工通信

2019-10-08 16:05:19

Redis數據庫系統

2013-05-03 13:59:18

視頻會議音頻音頻通話

2021-08-04 07:21:31

Hive 數據排查

2022-04-02 20:27:30

ETS操作系統鴻蒙
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 免费观看黄色一级片 | 天堂av免费观看 | 亚洲精品日韩精品 | 免费能直接在线观看黄的视频 | 国产精品免费看 | 日韩中文字幕在线观看视频 | 亚洲综合在线播放 | 91爱啪啪 | 久久一区二区三区四区 | 国产精品日日摸夜夜添夜夜av | 中文字幕国产视频 | 亚洲精品日韩在线 | 在线国产视频观看 | 久久久国产精品入口麻豆 | 久久999 | 国产欧美二区 | 亚洲乱码一区二区三区在线观看 | 国产视频久久久 | 国产精品免费视频一区 | 99精品国产一区二区三区 | 综合一区 | 91免费在线看 | 色综合久久久 | 新超碰97| 国产精品视频久久 | 成人欧美一区二区三区在线观看 | 亚洲欧洲一区二区 | 中文字幕国产 | 亚洲午夜精品一区二区三区他趣 | 一区二区三区av | 免费视频一区 | 日日干综合 | 欧美在线视频一区二区 | 欧美福利精品 | 日本色婷婷 | 欧洲性生活视频 | 99精彩视频 | 一级欧美黄色片 | 国产精品一区二区久久精品爱微奶 | 久久久久久av| 午夜视频在线免费观看 |