CompareAndSwap原子操作原理

在翻閱AQS(AbstractQueuedSynchronizer)類的過程中,發現其進行原子操作的時候采用的是CAS。涉及的代碼如下:

   1:    private static final Unsafe unsafe = Unsafe.getUnsafe();
   2:      private static final long stateOffset;
   3:      private static final long headOffset;
   4:      private static final long tailOffset;
   5:      private static final long waitStatusOffset;
   6:      private static final long nextOffset;
   7:  ?
   8:      static {
   9:          try {
  10:              stateOffset = unsafe.objectFieldOffset
  11:                  (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
  12:              headOffset = unsafe.objectFieldOffset
  13:                  (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
  14:              tailOffset = unsafe.objectFieldOffset
  15:                  (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
  16:              waitStatusOffset = unsafe.objectFieldOffset
  17:                  (Node.class.getDeclaredField("waitStatus"));
  18:              nextOffset = unsafe.objectFieldOffset
  19:                  (Node.class.getDeclaredField("next"));
  20:  ?
  21:          } catch (Exception ex) { throw new Error(ex); }
  22:      }
  23:  ?
  24:      /**
  25:       * CAS head field. Used only by enq.
  26:       */
  27:      private final boolean compareAndSetHead(Node update) {
  28:          return unsafe.compareAndSwapObject(this, headOffset, null, update);
  29:      }
  30:  ?
  31:      /**
  32:       * CAS tail field. Used only by enq.
  33:       */
  34:      private final boolean compareAndSetTail(Node expect, Node update) {
  35:          return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
  36:      }
  37:  ?
  38:      /**
  39:       * CAS waitStatus field of a node.
  40:       */
  41:      private static final boolean compareAndSetWaitStatus(Node node,
  42:                                                           int expect,
  43:                                                           int update) {
  44:          return unsafe.compareAndSwapInt(node, waitStatusOffset,
  45:                                          expect, update);
  46:      }
  47:  ?
  48:      /**
  49:       * CAS next field of a node.
  50:       */
  51:      private static final boolean compareAndSetNext(Node node,
  52:                                                     Node expect,
  53:                                                     Node update) {
  54:          return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
  55:      }

?

可以看到用到了compareAndSwapObject和compareAndSwapInt方法,那么究竟是怎么用其來實現原子操作的呢?

我們以compareAndSwapObject方法為例,其源碼大致如下:

   1:  UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject e_h, jobject x_h))
   2:    UnsafeWrapper("Unsafe_CompareAndSwapObject");
   3:    oop x = JNIHandles::resolve(x_h); //待更新的新值,也就是UpdateValue
   4:    oop e = JNIHandles::resolve(e_h); //期望值,也就是ExpectValue 
   5:    oop p = JNIHandles::resolve(obj); //待操作對象
   6:    HeapWord* addr = (HeapWord *)index_oop_from_field_offset_long(p, offset);//根據操作的對象和其在內存中的offset,計算出內存中具體位置
   7:    oop res = oopDesc::atomic_compare_exchange_oop(x, addr, e, true);// 如果操作對象中的值和e期望值一致,則更新存儲值為x,反之不更新
   8:    jboolean success  = (res == e); 
   9:    if (success) //滿足更新條件
  10:        update_barrier_set((void*)addr, x); // 更新存儲值為x
  11:    return success;
  12:  UNSAFE_END

從上述源碼可以看到,compareAndSwapObject方法中的第一個參數和第二個參數,用于確定待操作對象在內存中的具體位置的,然后取出值和第三個參數進行比較,如果相等,則將內存中的值更新為第四個參數的值,同時返回true,表明原子更新操作完畢。反之則不更新內存中的值,同時返回false,表明原子操作失敗。

同樣的,compareAndSwapInt方法也是相似的道理,第一個,第二個參數用來確定當前操作對象在內存中的存儲值,然后和第三個expect value比較,如果相等,則將內存值更新為第四個updaet value值。

由于原始的方法使用比較麻煩,所以在AQS中進行了封裝,大大簡化了操作:

   1:    private static final Unsafe unsafe = Unsafe.getUnsafe();
   2:      private static final long stateOffset;
   3:      private static final long headOffset;
   4:      private static final long tailOffset;
   5:      private static final long waitStatusOffset;
   6:      private static final long nextOffset;
   7:  ?
   8:      static {
   9:          try {
  10:              stateOffset = unsafe.objectFieldOffset
  11:                  (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
  12:              headOffset = unsafe.objectFieldOffset
  13:                  (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
  14:              tailOffset = unsafe.objectFieldOffset
  15:                  (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
  16:              waitStatusOffset = unsafe.objectFieldOffset
  17:                  (Node.class.getDeclaredField("waitStatus"));
  18:              nextOffset = unsafe.objectFieldOffset
  19:                  (Node.class.getDeclaredField("next"));
  20:  ?
  21:          } catch (Exception ex) { throw new Error(ex); }
  22:      }
  23:  ?
  24:      /**
  25:       * CAS head field. Used only by enq.
  26:       */
  27:      private final boolean compareAndSetHead(Node update) {
  28:          return unsafe.compareAndSwapObject(this, headOffset, null, update);
  29:      }
  30:  ?
  31:      /**
  32:       * CAS tail field. Used only by enq.
  33:       */
  34:      private final boolean compareAndSetTail(Node expect, Node update) {
  35:          return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
  36:      }
  37:  ?
  38:      /**
  39:       * CAS waitStatus field of a node.
  40:       */
  41:      private static final boolean compareAndSetWaitStatus(Node node,
  42:                                                           int expect,
  43:                                                           int update) {
  44:          return unsafe.compareAndSwapInt(node, waitStatusOffset,
  45:                                          expect, update);
  46:      }
  47:  ?
  48:      /**
  49:       * CAS next field of a node.
  50:       */
  51:      private static final boolean compareAndSetNext(Node node,
  52:                                                     Node expect,
  53:                                                     Node update) {
  54:          return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
  55:      }

可以在其他項目中作為小模塊進行引入并使用。這樣使用起來就非常方便了:

   1:  ?
   2:      /**
   3:       * Creates and enqueues node for current thread and given mode.
   4:       *
   5:       * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
   6:       * @return the new node
   7:       */
   8:      private Node addWaiter(Node mode) {
   9:          Node node = new Node(Thread.currentThread(), mode);
  10:          // Try the fast path of enq; backup to full enq on failure
  11:          Node pred = tail;
  12:          if (pred != null) {
  13:              node.prev = pred;
  14:              if (compareAndSetTail(pred, node)) {
  15:                  pred.next = node;
  16:                  return node;
  17:              }
  18:          }
  19:          enq(node);
  20:          return node;
  21:      }

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/386506.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/386506.shtml
英文地址,請注明出處:http://en.pswp.cn/news/386506.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

STemWin移植筆記

實現將STemWin圖形庫移植到STM32F103ZET中,記錄簡單的操作步驟,以便自己后續查閱。 1/ 從官網獲取STemWin庫的壓縮包en.stemwin.zip。 2/ 解壓后,在路徑STemWin_Library_V1.2.0\Libraries\STemWinLibrary532下可以找到庫文件。 3/ 移植前的準…

這些新技術你們都知道嗎?看這一篇就夠了!

前言 現在已經進入招聘季節,本篇文章旨在分享知名互聯網企業面試官面試方法和心得,希望通過本文的閱讀能給程序員帶來不一樣的面試體驗和感受,放松面試心態,積極備戰! 面試題 PS:由于文章篇幅問題&#x…

spring boot redis 分布式鎖

yml文件 redis:host: 127.0.0.1port: 40197password: 123456timeout: 5000database: 0jedis:pool:min-idle: 0max-idle: 8max-active: 8max-wait: -1 RedisConfig.java import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer…

mysql函數(二.數字函數)

數字函數 1.ROUND(X) 四舍五入取整數 ROUND(X,D) 四舍五入根據D幾,來保留幾位小數 (1)四舍五入取整數 select ROUND(1.567); 結果:2 (2)四舍五入根據D2,來保留2位小數 select ROUND(1.567,2)DESC; 結果:1.57 2.CEIL…

這份1307頁Android面試全套真題解析,源碼+原理+手寫框架

前言 前不久,幾個朋友聚會,談到了現在的后輩,我就說起了那個大三就已經拿到網易offer的小學弟。 這個學弟是00后,專升本進入我們學校的。進來后就非常努力,每次上課都是第一個到教室的,每次都是坐第一排&…

CAS的ABA問題描述 AtomicStampReference

CAS的ABA問題描述 在CAS操作的時候,其他線程將當前變量的值從A改成B,又改回A;CAS線程用期望值A與當前變量比較的時候,發現當前變量沒有變,于是CAS就將當前變量進行了交換操作,但其實當前變量改變過&#x…

[轉]OpenContrail 體系架構文檔

OpenContrail 體系架構文檔英文原文:http://opencontrail.org/opencontrail-architecture-documentation/ 翻譯者:KkBLuE知行合一 其微信號:kkbluepublic, SDNAP.com翻譯整理 OpenContrail 體系架構文檔 1 概述 1.1 使用案例 1…

這份354頁筆記的Android進階知識+大廠高頻面試題,絕對干貨

程序員與別的專業有所不同,其他專業都是越老越香,而程序員卻是一個例外,因為計算機技術更新太快,而且工作強度很大,因此大部分程序員只會寫 3 年代碼。3 年后要不晉升做項目經理,要么轉行,個別研…

原子性 atomic 類用法

當程序更新一個變量時,如果多線程同時更新這個變量,可能得到期望之外的值,比如變量i1,A線程更新i1,B線程也更新i1,經過兩個線程操作之后可能i不等于3,而是等于2。因為A和B線程在更新變量i的時候…

這是一份用心整理的Android面試總結,聰明人已經收藏了!

前言 本文想分享的是如何準備阿里面試的以及面試過程的所想所得,希望能幫到你。 首先,可能要讓你們失望的是,這篇文章不會有大篇幅的面試題答案。如果想要看這方面的內容,可以看我之前的文章。感謝關注 很多人準備面試的時候&a…

git 技能圖

---- 轉載于:https://www.cnblogs.com/WHWWHW/p/11136606.html

AtomicStampedReference源碼分析

之前的文章已經介紹過CAS的操作原理,它雖然能夠保證數據的原子性,但還是會有一個ABA的問題。 那么什么是ABA的問題呢?假設有一個共享變量“num”,有個線程A在第一次進行修改的時候把num的值修改成了33。修改成功之后,緊接著又立刻…

django:bootstrap table加載django返回的數據

bootstrap table加載表格數據有兩類方式: 一種通過data屬性的方式配置,一種是javascipt方式配置 這里看js配置方式: 1、當數據源為.json文件時 url參數寫上json文件的地址就行,但是json文件格式必須為json格式(2種): a:一種為json…

這是一份面向Android開發者的復習指南,成功入職字節跳動

前言 19年6月份從網易云音樂離開,放棄了留學機會,開始了人生的第一次創業,前后嘗試了兩個項目,因為個人能力與時機因素都失敗了,雖然沒能享受到創業所能夠帶來高杠桿物質上的回報,但是對個人軟技能和自我邊…

JVM啟動參數

不管是YGC還是Full GC,GC過程中都會對導致程序運行中中斷,正確的選擇不同的GC策略,調整JVM、GC的參數,可以極大的減少由于GC工作,而導致的程序運行中斷方面的問題,進而適當的提高Java程序的工作效率。但是調整GC是以個極為復雜的過程&#xf…

【UOJ 92】有向圖的強連通分量

【題目描述】: 有向圖強連通分量:在有向圖G中,如果兩個頂點vi,vj間(vi>vj)有一條從vi到vj的有向路徑,同時還有一條從vj到vi的有向路徑,則稱兩個頂點強連通(strongly connected)。如果有向圖G…

這篇文章可以滿足你80%日常工作!一線互聯網公司面經總結

前言 最近發現大家都喜歡看面試相關的文章,我也跟一波風,總結了一下我面試中所遇到的問題總結,分享一下面試中被問的最多的一些問題。 希望對正在找工作的朋友提供一些幫助。 好了話不多說,進入正題。 作為安卓開發者&#xff…

java并發synchronized 鎖的膨脹過程(鎖的升級過程)深入剖析(1)

我們先來說一下我們為什么需要鎖? 因為在并發情況為了保證線程的安全性,是在一個多線程環境下正確性的概念,也就是保證多線程環境下共享的、可修改的狀態的正確性(這里的狀態指的是程序里的數據),在java程…

MSCRM二次開發實現自動編號功能

功能描述:對客戶實體實現自動編號功能,1、2、3、4...... 自動編號存放于屬性accountnumber.原  理:在mscrm服務器用一個文本文件存放當前最新編號,每當創建客戶記錄時在PreCreate事件接口做以下步驟:1、鎖定文本文件…

這篇文章可以滿足你80%日常工作!成功入職騰訊

什么是中年危機 根據權威數據顯示,國內IT程序員鼎盛時期是在25-27歲左右,30歲對于程序員而言完全是一個38線,接著就是轉業轉崗的事情,這一點在業界也算是一個共識了。 大學畢業步入IT行業普遍年齡也是在22歲左右,然而…