zoukankan      html  css  js  c++  java
  • 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),独占锁,并且是不可重入锁。他们两的名字都是发明者的名字的缩写。

  • 相关阅读:
    Java集合框架
    常见异常--被解码的 URI 不是合法的编码
    BigDecimal使用以及异常处理
    文章标题--再识HTML5
    【转】解决$Proxy0 cannot be cast to java.sql.Connection异常
    Response-->cookie的添加和删除
    自定义标签---TLD约束文件格式说明
    XML约束文件---DTD文件
    JavaScript——注册表单参考模板(含参数格式校验)
    java的可序列化(转载)
  • 原文地址:https://www.cnblogs.com/CLAYJJ/p/10497150.html
Copyright © 2011-2022 走看看