對於Java多線程,接觸最多的莫過於使用synchronized,這個簡單易懂,可是這synchronized並不是性能最優的。今天我就簡單介紹一下幾種鎖。可能我下面講的時候其實不少東西不會特別深入,最好的方式是本身作實驗,把各類場景在代碼中實驗一下,這樣發發現不少細節。java
做爲Java中的輕量級鎖,當多線程中一個線程操做後能夠保證其餘線程可見,也就是書上所說的「可見性」,另一個就是「重排序」。所謂重排序指的是JVM對指令的優化。不少人可能在實際實驗中發現好像不是如此,最後的例子我也會說明這一點。安全
這個做爲Java中「重量級」的線程安全機制被你們所熟知,這個就不在這裏作解釋了。多線程
好,說一下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線程鎖機制有一個比較詳細的分析。