MCS锁和CLH锁
CLH鎖:自旋鎖,在上一個(gè)節(jié)點(diǎn)上等待,先上代碼:
1 public class CLHLock { 2 /** 3 * 保證原子性操作 4 * 5 */ 6 private AtomicReference<Node> tail = new AtomicReference<>(new Node()); 7 /** 8 * 每個(gè)線程的節(jié)點(diǎn)變量不同 9 * 10 */ 11 private ThreadLocal<Node> current = ThreadLocal.withInitial(() -> new Node()); 12 /** 13 * 記錄前驅(qū)節(jié)點(diǎn),并且復(fù)用此節(jié)點(diǎn)來(lái)防止死鎖: 14 * 假設(shè)不使用該節(jié)點(diǎn)的話,有T1,T2兩個(gè)線程,T1先lock成功,然后T2調(diào)用lock()時(shí)會(huì) 15 * 自旋在T1的節(jié)點(diǎn)的locked字段上,如果當(dāng)T1線程unlock()之后,(T2還沒(méi)獲取到CPU時(shí)間片), 16 * T1再次調(diào)用lock(),因?yàn)榇藭r(shí)tail的值是T2線程的節(jié)點(diǎn),其locked值為true,所以T1自旋等待 17 * T2釋放鎖,而此時(shí)的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設(shè)置為當(dāng)前線程的節(jié)點(diǎn),并獲取到上一個(gè)節(jié)點(diǎn),此操作為原子性操作 26 Node preNode = tail.getAndSet(node); 27 pred.set(preNode); 28 // 在前驅(qū)節(jié)點(diǎn)的locked字段上忙等待 29 while (preNode.locked); 30 31 } 32 33 34 public void unlock() { 35 Node node = current.get(); 36 // 將當(dāng)前線程節(jié)點(diǎn)的locked屬性設(shè)置為false,使下一個(gè)節(jié)點(diǎn)成功獲取鎖 37 node.locked = false; 38 current.set(pred.get()); 39 } 40 41 42 43 44 static class Node{ 45 volatile boolean locked; 46 } 47 }?
?
注意它的實(shí)例變量,tail為一個(gè)原子引用,所以在它上的操作都是原子性操作,它是所有線程共享的變量,與后面的兩個(gè)變量區(qū)分開(kāi),current是線程本地變量,它的值都和當(dāng)前線程有關(guān)。current記錄的是當(dāng)前線程的鎖情況。
加鎖時(shí),現(xiàn)將current的locked屬性設(shè)置為true,表示當(dāng)前線程需要獲取鎖,然后將tail中的值設(shè)置為當(dāng)前線程節(jié)點(diǎn),getAndSet方法設(shè)置新值并返回之前的值,這樣每個(gè)線程節(jié)點(diǎn)之間就有一條隱形的”鏈“關(guān)聯(lián)著,像一個(gè)鏈表。最后在上一個(gè)節(jié)點(diǎn)的locked屬性上自旋等待。
解鎖時(shí),只需把當(dāng)前節(jié)點(diǎn)的locked屬性設(shè)置為false,這樣緊接著的后面一個(gè)的線程就會(huì)成功的獲取鎖。
?
MCS鎖:
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,所以第一個(gè)加鎖線程,直接跳過(guò)判斷,加鎖成功 10 // tail中記錄的是當(dāng)前線程的節(jié)點(diǎn) 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 // 如果設(shè)置成功,說(shuō)明在此之前沒(méi)有線程進(jìn)行l(wèi)ock操作,直接return即可; 22 // 如果失敗,則說(shuō)明在此之前有線程進(jìn)行l(wèi)ock操作,需要自旋等待那個(gè)線程將自身節(jié)點(diǎn)設(shè)置為本線程節(jié)點(diǎn)的next, 23 // 然后進(jìn)行后面的操作。 24 if (tail.compareAndSet(node, null)) 25 return; 26 while (node.next == null); 27 } 28 // 通知下一個(gè)線程,使下一個(gè)線程加鎖成功 29 node.next.locked = false; 30 // 解鎖后需要將節(jié)點(diǎn)之間的關(guān)聯(lián)斷開(kāi),否則會(huì)產(chǎn)生內(nèi)存泄露 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),獨(dú)占鎖,并且是不可重入鎖。他們兩的名字都是發(fā)明者的名字的縮寫(xiě)。
轉(zhuǎn)載于:https://www.cnblogs.com/CLAYJJ/p/10497150.html
總結(jié)
- 上一篇: Java并发编程:volatile关键字
- 下一篇: C语言中二维数组名与数组地址、首行地址、