<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【轉(zhuǎn)】-Java CAS 原理剖析

      Java CAS 原理剖析

      本文轉(zhuǎn)載來自?卡巴拉的樹??Java CAS 原理剖析

      在Java并發(fā)中,我們最初接觸的應(yīng)該就是synchronized關(guān)鍵字了,但是synchronized屬于重量級鎖,很多時候會引起性能問題,volatile也是個不錯的選擇,但是volatile不能保證原子性,只能在某些場合下使用。

      synchronized這種獨占鎖屬于悲觀鎖,它是在假設(shè)一定會發(fā)生沖突的,那么加鎖恰好有用,除此之外,還有樂觀鎖,樂觀鎖的含義就是假設(shè)沒有發(fā)生沖突,那么我正好可以進行某項操作,如果要是發(fā)生沖突呢,那我就重試直到成功,樂觀鎖最常見的就是CAS

      我們在讀Concurrent包下的類的源碼時,發(fā)現(xiàn)無論是ReenterLock內(nèi)部的AQS,還是各種Atomic開頭的原子類,內(nèi)部都應(yīng)用到了CAS,最常見的就是我們在并發(fā)編程時遇到的i++這種情況。傳統(tǒng)的方法肯定是在方法上加上synchronized關(guān)鍵字:

      
      public class Test {
      
          public volatile int i;
      
          public synchronized void add() {
              i++;
          }
      }
      
      

      但是這種方法在性能上可能會差一點,我們還可以使用AtomicInteger,就可以保證i原子的++了。

      
      public class Test {
      
      
      
          public AtomicInteger i;
          
          public void add() {
              i.getAndIncrement();
          }
      }
      
      

      我們來看getAndIncrement的內(nèi)部:

      
      public final int getAndIncrement() {
          return unsafe.getAndAddInt(this, valueOffset, 1);
      }
      
      

      再深入到getAndAddInt():

      
      public final int getAndAddInt(Object var1, long var2, int var4) {
      
          int var5;
      
          do {
              var5 = this.getIntVolatile(var1, var2);
          } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
      
          return var5;
      
      }
      
      

      這里我們見到compareAndSwapInt這個函數(shù),它也是CAS縮寫的由來。那么仔細分析下這個函數(shù)做了什么呢?

      首先我們發(fā)現(xiàn)compareAndSwapInt前面的this,那么它屬于哪個類呢,我們看上一步getAndAddInt,前面是unsafe。這里我們進入的Unsafe類。這里要對Unsafe類做個說明。結(jié)合AtomicInteger的定義來說:

      
      public class AtomicInteger extends Number implements java.io.Serializable {
      
          private static final long serialVersionUID = 6214790243416807050L;
          
          // setup to use Unsafe.compareAndSwapInt for updates
          private static final Unsafe unsafe = Unsafe.getUnsafe();
          private static final long valueOffset;
          
          static {
              try {
                  valueOffset = unsafe.objectFieldOffset
                      (AtomicInteger.class.getDeclaredField("value"));
              } catch (Exception ex) { throw new Error(ex); }
          }
          
          private volatile int value;
          ...
      
      

      AtomicInteger數(shù)據(jù)定義的部分,我們可以看到,其實實際存儲的值是放在value中的,除此之外我們還獲取了unsafe實例,并且定義了valueOffset。再看到static塊,懂類加載過程的都知道,static塊的加載發(fā)生于類加載的時候,是最先初始化的,這時候我們調(diào)用unsafeobjectFieldOffsetAtomic類文件中獲取value的偏移量,那么valueOffset其實就是記錄value的偏移量的。

      再回到上面一個函數(shù)getAndAddInt,我們看var5獲取的是什么,通過調(diào)用unsafegetIntVolatile(var1, var2),這是個native方法,具體實現(xiàn)到JDK源碼里去看了,其實就是獲取var1中,var2偏移量處的值。var1就是AtomicIntegervar2就是我們前面提到的valueOffset,這樣我們就從內(nèi)存里獲取到現(xiàn)在valueOffset處的值了。

      現(xiàn)在重點來了,compareAndSwapInt(var1, var2, var5, var5 + var4)其實換成compareAndSwapInt(obj, offset, expect, update)比較清楚,意思就是如果obj內(nèi)的valueexpect相等,就證明沒有其他線程改變過這個變量,那么就更新它為update,如果這一步的CAS沒有成功,那就采用自旋的方式繼續(xù)進行CAS操作,取出乍一看這也是兩個步驟了啊,其實在JNI里是借助于一個CPU指令完成的。所以還是原子操作。

      1. CAS底層原理

      CAS底層使用JNI調(diào)用C代碼實現(xiàn)的,如果你有Hotspot源碼,那么在Unsafe.cpp里可以找到它的實現(xiàn):

      
      static JNINativeMethod methods_15[] = {
      
          //省略一堆代碼...
          {CC"compareAndSwapInt",  CC"("OBJ"J""I""I"")Z",      FN_PTR(Unsafe_CompareAndSwapInt)},
          {CC"compareAndSwapLong", CC"("OBJ"J""J""J"")Z",      FN_PTR(Unsafe_CompareAndSwapLong)},
          //省略一堆代碼...
      };
      
      

      我們可以看到compareAndSwapInt實現(xiàn)是在Unsafe_CompareAndSwapInt里面,再深入到Unsafe_CompareAndSwapInt:

      
      UNSAFE_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
      
      

      p是取出的對象,addr是p中offset處的地址,最后調(diào)用了Atomic::cmpxchg(x, addr, e), 其中參數(shù)x是即將更新的值,參數(shù)e是原內(nèi)存的值。代碼中能看到cmpxchg有基于各個平臺的實現(xiàn),這里我選擇Linux X86平臺下的源碼分析:

      
      inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {
      
        int mp = 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");
        return exchange_value;
      }
      
      

      這是一段小匯編,__asm__說明是ASM匯編,__volatile__禁止編譯器優(yōu)化

      
      // Adding a lock prefix to an instruction on MP machine
      
      #define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "
      
      

      os::is_MP判斷當前系統(tǒng)是否為多核系統(tǒng),如果是就給總線加鎖,所以同一芯片上的其他處理器就暫時不能通過總線訪問內(nèi)存,保證了該指令在多處理器環(huán)境下的原子性。

      在正式解讀這段匯編前,我們來了解下嵌入?yún)R編的基本格式:

      
      asm ( assembler template
      
          : output operands                  /* optional */
      
          : input operands                   /* optional */
      
          : list of clobbered registers      /* optional */
      
          );
      
      
      • template就是cmpxchgl %1,(%3)表示匯編模板
      • output operands表示輸出操作數(shù),=a對應(yīng)eax寄存器
      • input operand 表示輸入?yún)?shù),%1 就是exchange_value, %3dest, %4就是mpr表示任意寄存器,a還是eax寄存器
      • list of clobbered registers就是些額外參數(shù),cc表示編譯器cmpxchgl的執(zhí)行將影響到標志寄存器, memory告訴編譯器要重新從內(nèi)存中讀取變量的最新值,這點實現(xiàn)了volatile的感覺。

      那么表達式其實就是cmpxchgl exchange_value ,dest,我們會發(fā)現(xiàn)%2也就是compare_value沒有用上,這里就要分析cmpxchgl的語義了。cmpxchgl末尾l表示操作數(shù)長度為4,上面已經(jīng)知道了。cmpxchgl會默認比較eax寄存器的值即compare_valueexchange_value的值,如果相等,就把dest的值賦值給exchange_value,否則,將exchange_value賦值給eax。具體匯編指令可以查看Intel手冊CMPXCHG

      最終,JDK通過CPU的cmpxchgl指令的支持,實現(xiàn)AtomicIntegerCAS操作的原子性。

      2. CAS 的問題

      1. ABA問題

      CAS需要在操作值的時候檢查下值有沒有發(fā)生變化,如果沒有發(fā)生變化則更新,但是如果一個值原來是A,變成了B,又變成了A,那么使用CAS進行檢查時會發(fā)現(xiàn)它的值沒有發(fā)生變化,但是實際上卻變化了。這就是CAS的ABA問題。 常見的解決思路是使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加一,那么A-B-A 就會變成1A-2B-3A。 目前在JDK的atomic包里提供了一個類AtomicStampedReference來解決ABA問題。這個類的compareAndSet方法作用是首先檢查當前引用是否等于預(yù)期引用,并且當前標志是否等于預(yù)期標志,如果全部相等,則以原子方式將該引用和該標志的值設(shè)置為給定的更新值。

      1. 循環(huán)時間長開銷大

      上面我們說過如果CAS不成功,則會原地自旋,如果長時間自旋會給CPU帶來非常大的執(zhí)行開銷。

      posted @ 2024-07-10 09:31  booleandev  閱讀(22)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 99精品视频在线观看免费蜜桃| 午夜在线欧美蜜桃| 影音先锋女人AA鲁色资源| 国内熟妇人妻色在线视频| 亚洲国产成人精品女人久久久| 天天躁日日躁狠狠躁性色avq| 欧美性猛交xxxx乱大交丰满| 国产成人av乱码在线观看| 一级国产在线观看高清| 欧美老熟妇又粗又大| 九九热在线视频观看最新| 日韩乱码人妻无码中文字幕视频 | 97色伦97色伦国产| 国产亚洲精品VA片在线播放| 在线免费观看毛片av| 亚洲免费观看一区二区三区| 精品天堂色吊丝一区二区| 人人人爽人人爽人人av| 亚洲综合成人av在线| 国产午夜精品福利免费看| 久久久久无码精品国产h动漫| 免费人妻无码不卡中文18禁| 欧美巨大极度另类| 久久精品国产清自在天天线| 国产日产精品系列| 国产精品一区二区国产馆| 永久免费av无码网站直播| 欧美18videosex性欧美tube1080 | 久久人人97超碰人人澡爱香蕉| 日本大片在线看黄a∨免费| 康乐县| 亚洲理论在线A中文字幕| 国产精品午夜精品福利| 亚洲AV午夜电影在线观看| 亚洲熟女乱综合一区二区| 人人澡人人妻人人爽人人蜜桃| 日本道之久夂综合久久爱| 国产超高清麻豆精品传媒麻豆精品 | 九九热精品在线观看| 国产精品日韩av在线播放| 精品久久久久无码|