CLH鎖:自旋鎖,在上一個節點上等待,先上代碼:node
1 public class CLHLock { 2 /** 3 * 保證原子性操做 4 * 5 */ 6 private AtomicReference<Node> tail = new AtomicReference<>(new Node()); 7 /** 8 * 每一個線程的節點變量不一樣 9 * 10 */ 11 private ThreadLocal<Node> current = ThreadLocal.withInitial(() -> new Node()); 12 /** 13 * 記錄前驅節點,而且複用此節點來防止死鎖: 14 * 假設不使用該節點的話,有T1,T2兩個線程,T1先lock成功,而後T2調用lock()時會 15 * 自旋在T1的節點的locked字段上,若是當T1線程unlock()以後,(T2還沒獲取到CPU時間片), 16 * T1再次調用lock(),由於此時tail的值是T2線程的節點,其locked值爲true,因此T1自旋等待 17 * T2釋放鎖,而此時的T2還在等T1釋放鎖,這就形成了死鎖。 18 * 19 */ 20 private ThreadLocal<Node> pred = new ThreadLocal<>(); 21 22 public void lock() { 23 Node node = current.get(); 24 node.locked = true; 25 // 將tail設置爲當前線程的節點,並獲取到上一個節點,此操做爲原子性操做 26 Node preNode = tail.getAndSet(node); 27 pred.set(preNode); 28 // 在前驅節點的locked字段上忙等待 29 while (preNode.locked); 30 31 } 32 33 34 public void unlock() { 35 Node node = current.get(); 36 // 將當前線程節點的locked屬性設置爲false,使下一個節點成功獲取鎖 37 node.locked = false; 38 current.set(pred.get()); 39 } 40 41 42 43 44 static class Node{ 45 volatile boolean locked; 46 } 47 }
注意它的實例變量,tail爲一個原子引用,因此在它上的操做都是原子性操做,它是全部線程共享的變量,與後面的兩個變量區分開,current是線程本地變量,它的值都和當前線程有關。current記錄的是當前線程的鎖狀況。spa
加鎖時,現將current的locked屬性設置爲true,表示當前線程須要獲取鎖,而後將tail中的值設置爲當前線程節點,getAndSet方法設置新值並返回以前的值,這樣每一個線程節點之間就有一條隱形的」鏈「關聯着,像一個鏈表。最後在上一個節點的locked屬性上自旋等待。線程
解鎖時,只需把當前節點的locked屬性設置爲false,這樣緊接着的後面一個的線程就會成功的獲取鎖。code
MCS鎖:blog
1 public class MCSLock { 2 AtomicReference<Node> tail = new AtomicReference<>(); 3 ThreadLocal<Node> current = ThreadLocal.withInitial(() -> new Node()); 4 5 6 public void lock() { 7 Node node = current.get(); 8 Node pred = tail.getAndSet(node); 9 // pred的初始值爲null,因此第一個加鎖線程,直接跳過判斷,加鎖成功 10 // tail中記錄的是當前線程的節點 11 if (pred != null) { 12 pred.next = node; 13 while (node.locked); 14 } 15 16 } 17 18 public void unlock() { 19 Node node = current.get(); 20 if (node.next == null) { 21 // 若是設置成功,說明在此以前沒有線程進行lock操做,直接return便可; 22 // 若是失敗,則說明在此以前有線程進行lock操做,須要自旋等待那個線程將自身節點設置爲本線程節點的next, 23 // 而後進行後面的操做。 24 if (tail.compareAndSet(node, null)) 25 return; 26 while (node.next == null); 27 } 28 // 通知下一個線程,使下一個線程加鎖成功 29 node.next.locked = false; 30 // 解鎖後須要將節點之間的關聯斷開,不然會產生內存泄露 31 node.next = null; 32 } 33 34 35 static class Node{ 36 volatile boolean locked = true; 37 volatile Node next; 38 } 39 40 }
CLH和MCS鎖都是自旋鎖,公平鎖(保證FIFO),獨佔鎖,而且是不可重入鎖。他們兩的名字都是發明者的名字的縮寫。內存