鎖分類(獨佔鎖、分拆鎖、分離鎖、分散式鎖)
阿新 • • 發佈:2018-12-14
- 在共享記憶體的多處理器體系架構中,每個處理器都擁有自己的快取,並且定期地與主記憶體進行協調。
- 在不同的處理器架構中提供了不同級別的快取一致性(Cache Coherence),
- 其中一部分只提供最小的保證,即允許不同的處理器在任意時刻從同一個儲存位置上看到不同的值。
- 作業系統、編譯器以及執行時(有時甚至包括應用程式)需要彌合這種在硬體能力與執行緒安全之間的差異。
java記憶體模型
- 抽象出線程獨有的執行記憶體和共有的主儲存
- 為了使java開發人員無須關心不同架構記憶體模型之間的差異,
- Java還提供了自己的記憶體模型,並且JVM通過在適當的位置上插入記憶體柵欄(記憶體屏障)
- 執行緒中的變數何時同步回到記憶體是不可預期的
- java記憶體模型規定,通過關鍵詞”synchronized“、”volatile“可以讓java保證某些約束。
- “volatile” - 保證讀寫的都是主記憶體變數。
- “synchronized” - 保證在塊開始時,都同步主記憶體值到工作記憶體,而快結束時,將工作記憶體同步會主記憶體。
指令重排序::
public class PossibleReordering { static int x = 0, y = 0; static int a = 0, b = 0; public static void main(String[] args) throws InterruptedException { Thread one = new Thread(new Runnable() { @Override public void run() { a = 1; x = b; } }); Thread two = new Thread(new Runnable() { @Override public void run() { b = 2; y = a; } }); one.start(); two.start(); one.join(); two.join(); System.out.println("x:" + x + ",y:" + y); } }
- 執行結果,一般人可能認為是1,1;真正的執行結果可能每次都不一樣。
- 拜JMM重排序所賜,JMM使得不同執行緒的操作順序是不同的,
- 從而導致在缺乏同步的情況下,要推斷操作的執行結果將變得更加複雜。
- 同步將限制編譯器和硬體執行時對記憶體操作重排序的方式。
鎖synchronized
- 鎖實現了對臨界資源的互斥訪問,是嚴格的排它鎖、互斥鎖。
- JVM規範通過兩個記憶體屏障(memory barrier)命令來實現排它邏輯。
- 記憶體屏障可以理解成順序執行的一組CPU指令,完全無視指令重排序。
- 類鎖(鎖是TestSTatic.class物件)、物件鎖
獨佔鎖
- 如果你不敢確定該用什麼鎖,就用這個吧,在保證正確的前提下,後續在提高開發效率。
分拆鎖
- 如果將這些鎖請求分到更多的鎖上,就能有效降低鎖競爭程度。
- 由於等待而被阻塞的執行緒將更少,從而可伸縮性將提高。
- 同時使用倆
分離鎖
- 在某些情況下,可以將鎖分解技術進一步擴充套件為對一組獨立物件上的鎖進行分解,這種情況稱為鎖分段
- 例如ConcurrencyHashMap是有一個包含16個鎖的陣列實現
- 鎖分段的劣勢在於:
- 與採用單個鎖來實現獨佔訪問相比,
- 要獲取多個鎖來實現獨佔訪問將更加困難並且開銷更高,
- 比如計算size、重hash。
分散式鎖
- zookeeper,判斷臨時節點是否存在,存在就說明已經有人爭搶到鎖;
- 不存在就建立節點,表明擁有該鎖。
volatile
- volatile是比synchronized更輕量級的同步原語,
- volatile可以修飾例項變數、靜態變數、以及陣列變數(引用)。
- 被volatile修飾的變數,JVM規範規定,一個執行緒在修改完,另外的執行緒能讀取最新的值。
- 僅僅保證可見性,不保證原子性