<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    xylz,imxylz

    關注后端架構、中間件、分布式和并發編程

       :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      111 隨筆 :: 10 文章 :: 2680 評論 :: 0 Trackbacks

    在JDK 5之前Java語言是靠synchronized關鍵字保證同步的,這會導致有鎖(后面的章節還會談到鎖)。

    鎖機制存在以下問題:

    (1)在多線程競爭下,加鎖、釋放鎖會導致比較多的上下文切換和調度延時,引起性能問題。

    (2)一個線程持有鎖會導致其它所有需要此鎖的線程掛起。

    (3)如果一個優先級高的線程等待一個優先級低的線程釋放鎖會導致優先級倒置,引起性能風險。

    volatile是不錯的機制,但是volatile不能保證原子性。因此對于同步最終還是要回到鎖機制上來。

    獨占鎖是一種悲觀鎖,synchronized就是一種獨占鎖,會導致其它所有需要鎖的線程掛起,等待持有鎖的線程釋放鎖。而另一個更加有效的鎖就是樂觀鎖。所謂樂觀鎖就是,每次不加鎖而是假設沒有沖突而去完成某項操作,如果因為沖突失敗就重試,直到成功為止。

    CAS 操作

    上面的樂觀鎖用到的機制就是CAS,Compare and Swap。

    CAS有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則什么都不做。

    非阻塞算法 (nonblocking algorithms)

    一個線程的失敗或者掛起不應該影響其他線程的失敗或掛起的算法。

    現代的CPU提供了特殊的指令,可以自動更新共享數據,而且能夠檢測到其他線程的干擾,而 compareAndSet() 就用這些代替了鎖定。

    拿出AtomicInteger來研究在沒有鎖的情況下是如何做到數據正確性的。

    private volatile int value;

    首先毫無以為,在沒有鎖的機制下可能需要借助volatile原語,保證線程間的數據是可見的(共享的)。

    這樣才獲取變量的值的時候才能直接讀取。

    public final int get() {
            return value;
        }

    然后來看看++i是怎么做到的。

    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    在這里采用了CAS操作,每次從內存中讀取數據然后將此數據和+1后的結果進行CAS操作,如果成功就返回結果,否則重試直到成功為止。

    而compareAndSet利用JNI來完成CPU指令的操作。

    public final boolean compareAndSet(int expect, int update) {  
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
        }

    整體的過程就是這樣子的,利用CPU的CAS指令,同時借助JNI來完成Java的非阻塞算法。其它原子操作都是利用類似的特性完成的。

    而整個J.U.C都是建立在CAS之上的,因此對于synchronized阻塞算法,J.U.C在性能上有了很大的提升。參考資料的文章中介紹了如果利用CAS構建非阻塞計數器、隊列等數據結構。

    CAS看起來很爽,但是會導致“ABA問題”。

    CAS算法實現一個重要前提需要取出內存中某時刻的數據,而在下時刻比較并替換,那么在這個時間差類會導致數據的變化。

    比如說一個線程one從內存位置V中取出A,這時候另一個線程two也從內存中取出A,并且two進行了一些操作變成了B,然后two又將V位置的數據變成A,這時候線程one進行CAS操作發現內存中仍然是A,然后one操作成功。盡管線程one的CAS操作成功,但是不代表這個過程就是沒有問題的。如果鏈表的頭在變化了兩次后恢復了原值,但是不代表鏈表就沒有變化。因此前面提到的原子操作AtomicStampedReference/AtomicMarkableReference就很有用了。這允許一對變化的元素進行原子操作。

     

     

    參考資料:

    (1)非阻塞算法簡介

    (2)流行的原子

     



    ©2009-2014 IMXYLZ |求賢若渴
    posted on 2010-07-04 18:03 imxylz 閱讀(48006) 評論(19)  編輯  收藏 所屬分類: J2EE

    評論

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4[未登錄] 2010-07-05 15:24 nick
    繼續繼續  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4 2010-11-13 18:41 HyviTan
    Hi

    文章結尾的兩個參考文獻鏈接失效了。  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4 2011-03-07 20:51 caihj
    “上面的樂觀鎖用到的機制就是CAS,Compare and Swap”
    是Swap?不是Set?
      回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4 2011-05-04 13:00 kick
    請教博主,從網上找了一段模擬CAS的代碼,能夠實現i++功能,改了一下。開了10000個線程運行,結果是錯誤的,達不到10000. 為什么?
    public class CasCounter {
    private SimulatedCAS simulatedCAS = null;
    public int getValue(){
    return simulatedCAS.getValue();
    }

    public int increment(){
    int oldValue = simulatedCAS.getValue();
    while(simulatedCAS.compareAndSwap(oldValue, oldValue+1) != oldValue)
    oldValue = simulatedCAS.getValue();
    return oldValue;
    }

    public CasCounter(SimulatedCAS cas){
    this.simulatedCAS = cas;
    }

    public static void main(String[] args) throws InterruptedException {
    CasCounter counter = new CasCounter(new SimulatedCAS());
    Thread[] ts = new Thread[10000];
    for(int i = 0; i <10000; i++){
    ts[i] = new CounterThread(counter);

    }
    for(Thread t : ts){
    t.start();
    }

    for(Thread t : ts){
    t.join();
    }

    System.out.println("counter: "+ counter.getValue());
    }
    }

    public class CounterThread extends Thread {
    CasCounter counter = null;

    public CounterThread(CasCounter counter) {

    this.counter = counter;
    }

    @Override
    public void run() {
    counter.increment();
    }
    }

    public class SimulatedCAS {

    private volatile int value = 0;
    public synchronized int getValue(){
    return value;

    }
    public synchronized int compareAndSwap(int expectedValue, int newValue){
    if(value == expectedValue)
    value = newValue;
    return expectedValue;
    }
    }
      回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4[未登錄] 2011-08-09 20:07 xxx
    @kick
    問題可能出在CasCounter.increment()函數中。
    多個線程可能取到相同的 oldValue,會導致線程修改失效。
    如果給這個函數加上 synchronized差不多就可以了。

    不過郁悶的是,加上synchronized以后,也會出現達不到10000的情況。
    不過比不加synchronized的情況好的多。不知道是為什么了  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4 2011-08-09 21:29 xylz
    @xxx
    @kick

    出錯的地方在于CAS操作,稍微改動一下:
    public synchronized int compareAndSwap(int expectedValue, int newValue) {
    if (value == expectedValue) {
    value = newValue;
    return expectedValue;
    }
    return - expectedValue;
    }
    就可以得到正確結果的。通常CAS操作可以返回true/false,如果操作成功返回true,否則返回false。原來的代碼是不管操作是否成功都返回了期待值,所以就改變了操作邏輯。
      回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4 2011-08-11 15:49 zhangxl
    @xylz
    這里的模擬都使用了synchronized(內在鎖)關鍵字,模擬CAS還有什么意義呢?引入CAS的目的不就是為了較少鎖的競爭,提高多線程并發的吞吐率嗎?
    我覺得要模擬也應該像AQS那樣,比如,這是AQS的源碼中狀態變量的原子操作:

    A.Q.S里面包含了一個存儲同步狀態的變量,它的聲明如下:

    private volatile int state;

    這里采用了volatile修飾符的原因是為了保證對state變量的寫對所有的線程都是可見的。但是大家都知道,volatile只能保證變量的可見性,不能保證對變量操作的原子性,所以A.Q.S里面就采用了CAS(Compare And Swap)操作來更新state變量的值,代碼如下:

    protected final boolean compareAndSetState(int expect, int update)
    { // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }


    個人覺得這樣才能模擬出CAS的本質,原子特性。
      回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4 2011-08-13 20:18 yintiefu
    @kick
    修改2個地方的代碼

    1.
    public int increment() {

    for(;;){
    int oldValue = simulatedCAS.getValue();
    if(simulatedCAS.compareAndSwap(oldValue,oldValue+1)){
    oldValue = simulatedCAS.getValue();
    return oldValue;
    }
    }
    }

    2.
    public synchronized boolean compareAndSwap(int expectedValue, int newValue) {
    if (value == expectedValue){
    value = newValue;
    return true;
    }
    return false;
    }  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4 2011-08-26 10:53 imtxt
    @zhangxl
    您講的是上面的回復貼出的代碼?本來那就是拿來做好玩用的吧。  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4 2012-07-09 11:39 waitingfortime
    @xylz
    這個代碼不做修改,也可以得到正確的數啊?怎么得不到呢  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4[未登錄] 2013-03-03 14:40 teasp
    “獨占鎖是一種悲觀鎖”,這是錯誤的說法。  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4 2013-03-06 21:44 asdf
    @teasp
    別話說一半,解釋一下  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4[未登錄] 2013-03-07 12:54 teasp
    @asdf
    獨占鎖既可以是悲觀的也可以是樂觀的呀,用CAS實現的獨占鎖不就是樂觀的?  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4[未登錄] 2013-06-16 13:24 flying
    用博主的 int compareAndSwap方法返回- expectedValue,有時會出現9999,用boolean是對的,應該是expectedValue為零時,-0和0是相同的值,導致沒有加成功。  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4[未登錄] 2013-06-16 13:31 flying
    @xylz
    SimulatedCAS 類的value 是volatile ,getValue的synchronized 關鍵字是不是可以去掉了?
      回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4 2014-08-06 21:04 vanjayzhou
    借這里問個問題哈,java 當中的cas問題,java concurrent 包的實現是依賴 CAS準則實現的,對AtomicInteger 方法中的 compareAndSet方法有些疑惑。
    public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    unsafe.compareAndSwapInt方法是本地方法, 我考慮這么一種情況:
    兩個線程T1, T2 同時取了主內存中A, T1調用compareAndSet 方法成功,將A變成 A1, 因為A值是同時從內存中取出來的,那T2調用compareAndSet方法也會成功,將A變成A2. 那結果不是不正確了么。
    幫我分析一下啊。
    謝啦
      回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4 2014-08-08 10:13 imxylz
    @vanjayzhou
    CAS的CPU原語保證A修改時,T2修改會失敗。所以T2才需要循環操作,直到成功。  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4[未登錄] 2014-10-31 16:58 JJZHK
    @vanjayzhou
    老版的CPU會進行鎖總線,新版的CPU進行的是緩存鎖定。所以T2修改的時候肯定會失敗的,因為共享內存已經被CPU鎖定了。  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (5): 原子操作 part 4[未登錄] 2016-01-06 08:40 William Chen
    沒錯是交換設置。  回復  更多評論
      


    ©2009-2014 IMXYLZ
    主站蜘蛛池模板: 成人免费福利视频| 国产片免费在线观看| 成人免费在线看片| 久久丫精品国产亚洲av不卡 | a毛片基地免费全部视频| 亚洲精品成人久久久| 亚洲成人一级电影| 国产午夜精品理论片免费观看 | 女人18毛片水真多免费看| 亚洲日产无码中文字幕| 亚洲AV无码AV日韩AV网站| 久久午夜免费鲁丝片| 亚洲高清国产拍精品青青草原| 亚洲国语在线视频手机在线| 四虎在线免费视频| 亚洲av永久无码精品表情包| 日韩精品视频在线观看免费| 免费观看黄网站在线播放| 亚洲视频在线观看不卡| 国产午夜精品免费一区二区三区 | 亚洲综合伊人久久综合| 粉色视频成年免费人15次| 我要看免费的毛片| 亚洲福利一区二区| 人妻无码一区二区三区免费| 久久精品国产亚洲7777| 免费国产黄网站在线观看动图| 亚洲伊人成无码综合网 | 大桥未久亚洲无av码在线| 2021免费日韩视频网| 久久水蜜桃亚洲AV无码精品 | 亚洲人成电影亚洲人成9999网| 国产精品99爱免费视频| 亚洲精品无码永久在线观看| 国产成人久久AV免费| 亚洲日韩一区精品射精| 免费鲁丝片一级在线观看| 亚洲GV天堂GV无码男同| 国产免费直播在线观看视频| 在线观看亚洲视频| 91亚洲国产成人久久精品网站|