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),独占锁,而且是不可重入锁。他们两的名字都是发明者的名字的缩写。内存