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

  • 相关阅读:
    C#面向对象设计模式纵横谈 笔记10 Decorator 装饰(结构型模式)
    [译]WPF 应用程序和MVVM设计模式 ——Josh Smith
    熙熙如何对待JavaScript(绝好的一套针对初学者的JavaScript教程)推荐教程下载PDFCHM电子书经熙熙筛选整理
    熙熙如何在Vmware里安装Ubantu9.10Alpha6(虚拟机安装Linux)
    熙熙如何设置WinCE仿真模拟器(网络、串口、耳机)
    熙熙我的博客开通啦
    熙熙SQLCE类熙熙
    熙熙WebBrowser判断登录成功WebBrowser404错误500错误屏蔽消息窗口Webbrowser判断是否加载成功
    熙熙如何用USB将WinCE与PC同步(天嵌开发板,ARM)
    推荐我一个很喜欢的音乐软件 多米音乐盒
  • 原文地址:https://www.cnblogs.com/CLAYJJ/p/10497150.html
Copyright © 2020-2023  润新知