Java多線程併發鎖和原子操做,你真的瞭解嗎?

 

前言        

        對於Java多線程,接觸最多的莫過於使用synchronized,這個簡單易懂,可是這synchronized並不是性能最優的。今天我就簡單介紹一下幾種鎖。可能我下面講的時候其實不少東西不會特別深入,最好的方式是本身作實驗,把各類場景在代碼中實驗一下,這樣發發現不少細節。java

volatile

        做爲Java中的輕量級鎖,當多線程中一個線程操做後能夠保證其餘線程可見,也就是書上所說的「可見性」,另一個就是「重排序」。所謂重排序指的是JVM對指令的優化。不少人可能在實際實驗中發現好像不是如此,最後的例子我也會說明這一點。安全

synchronized

        這個做爲Java中「重量級」的線程安全機制被你們所熟知,這個就不在這裏作解釋了。多線程

java.util.concurrent.locks.ReentrantLock

        java.util.concurrent.中是JDK1.5中出的對於一些併發操做的類庫,其中包括不少同窗很喜歡的原子類,好比說AtomicInteger。它裏面原理就是一個CAS,這裏就不作過多的闡述,有興趣的能夠看看源碼。

 

       好,說一下ReentrantLock,這個鎖主要是能顯示的添加鎖和釋放鎖,好處是更加靈活,可以更加準確的控制鎖,也能確保系統的穩定,好比說「重連」。後面代碼會有使用到。 併發

 實際場景

       上面介紹完了幾種鎖,下面用具體的代碼來看看幾種鎖的實際用法,以及各類表現形式。代碼有點長,這裏最好本身實驗一下,而後看看結果,並思考這個結果。性能

輸出結果:優化

            i>>>>>381890url

            vi>>>>>353610線程

            ai>>>>>400000排序

            si>>>>>392718ip

            ri>>>>>392658

 從上面的輸出結果來看真是讓人大感意外:只有原子操做AtomicInteger的結果保證了多線程的安全性,而其餘無論是用輕量級的volatile仍是重量級的synchronized都沒有達到咱們想要的效果。這也讓我產生了大在的懷疑。難道問題真的這麼蹊蹺?

       從這裏不難看出除了AtomicInteger用的是其本身的方法而其餘都是用到了Java的語法糖++操做。而這讓我想起了++操做並不是原子操做,而可能在其中間操做致使了其餘線程對其餘進行了修改,雖然一樣的問題我在《Think in Java》中也找到能夠佐證的例子。這裏有一個問題就是synchronized:由於我對si已經加了synchronized操做,可是輸出的結果使人意外,難道還會有問題?這讓我想把這段代碼編譯成字節碼的衝動。好吧,下面看字節碼(這裏我單獨把synchronized這一段操做抽出來,做爲分析,其餘幾個就算了,否則編譯後的字節碼有點多)

      爲了方便看,先貼出源代碼

下面是字節碼,爲了節省篇幅,一些不重要的部分我將不貼出

從這裏一看從monitorenter進入安全區到monitorexit出安全區沒有發現si是處於中間狀態的,那又是在哪出的問題呢?這裏簡單說一下,歸根結底仍然是(++)操做非原子操做,但是不少人疑惑了,這裏不是加鎖了嗎?廢話很少說,在個人深刻探析Java線程鎖機制有一個比較詳細的分析。

相關文章
相關標籤/搜索