JAVA高併發的三種實現
提到鎖,大家肯定想到的是sychronized關鍵字。是用它可以解決一切併發問題,但是,對於系統吞吐量要求更高的話,我們這提供幾個小技巧。幫助大家減小鎖顆粒度,提高併發能力。
初級技巧-樂觀鎖
樂觀鎖使用的場景是,讀不會衝突,寫會衝突。同時讀的頻率遠大於寫。
悲觀鎖的實現:
悲觀的認為所有程式碼執行都會有併發問題,所以將所有程式碼塊都用sychronized鎖住
樂觀鎖的實現:
樂觀的認為在讀的時候不會產生衝突為題,在寫時新增鎖。所以解決的應用場景是讀遠大於寫時的場景。
中級技巧-String.intern()
樂觀鎖不能很好的解決大量的寫衝突的問題,但是很多場景下,鎖只是針對某個使用者或者某個訂單。 比如一個使用者先建立session,才能進行後面的操作,但是由於網路的問題,建立session的請求和後續請求幾乎同時到達,而並行執行緒可能會先處理後面的請求。一般情況需要對使用者sessionMap加鎖,比如上面的樂觀鎖。在這樣的場景下,可以將鎖限定在使用者本身上,即原來的
這個比較類似行鎖和資料庫表鎖的概念。顯然行鎖的併發能力比表鎖的高很多。
實用String.intern();是這種方式的具體實現。類String維護了一個字串池。當呼叫intern方法時,如果池已經包含一個等於此String物件的字串(該物件由equals(Object)方法確定),則返回池中的字串。可見,當String 相同時,總返回同一個物件,因此就實現了對同一使用者加鎖。由於所的顆粒度侷限於具體使用者,使得系統獲得最大程度的併發。
CopyOnWriteMap?
既然說到了“類似於資料庫中的行鎖的概念”,就不得不提一下MVCC,Java中CopyOnWrite類實現了MVCC。Copy On Write是這樣一種機制。當我們讀取共享資料的時候,直接讀取,不需要同步。當我們修改資料的時候,我們就把當前資料Copy一份副本,然後在這個副本 上進行修改,完成之後,再用修改後的副本,替換掉原來的資料。這種方法就叫做Copy On Write。
但是,,,JDK並沒有提供CopyOnWriteMap,為什麼?下面有個很好的回答,那就是已經有了ConcurrentHashMap,為什麼還需要CopyOnWriteMap?
高階技巧 - 類ConcurrentHashMap
String.inter()的缺陷是類 String 維護一個字串池是放在JVM perm區的,如果使用者數特別多,導致放入字串池的String不可控,有可能導致OOM錯誤或者過多的Full GC。怎麼樣能控制鎖的個數,同時減小粒度鎖呢?直接使用Java ConcurrentHashMap?或者你想加入自己更精細的控制?那麼可以借鑑ConcurrentHashMap的方式,將需要加鎖的物件分為多個bucket,每個bucket加一個鎖,虛擬碼如下: