MCS锁和CLH锁

CLH锁:自旋锁,在上一个节点上等待,先上代码:

 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记录的是当前线程的锁情况。

加锁时,现将current的locked属性设置为true,表示当前线程需要获取锁,然后将tail中的值设置为当前线程节点,getAndSet方法设置新值并返回之前的值,这样每个线程节点之间就有一条隐形的”链“关联着,像一个链表。最后在上一个节点的locked属性上自旋等待。

解锁时,只需把当前节点的locked属性设置为false,这样紧接着的后面一个的线程就会成功的获取锁。

 

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,所以第一个加锁线程,直接跳过判断,加锁成功
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),独占锁,并且是不可重入锁。他们两的名字都是发明者的名字的缩写。

posted @ 2019-03-19 11:15  CLAYJJ  阅读(723)  评论(0编辑  收藏  举报