并发系列(3)之 CLH、MCS 队列锁简介

并发系列(3)之 CLH、MCS 队列锁简介这篇博客主要是作为AbstractQueuedSynchronizer的背景知识介绍;平时接触也非常的少,如果你不感兴趣可以跳过;但是了解一下能更加的清楚AQS的设计思路;一、自旋锁简介通

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

这篇博客主要是作为 AbstractQueuedSynchronizer 的背景知识介绍;平时接触也非常的少,如果你不感兴趣可以跳过;但是了解一下能更加的清楚 AQS 的设计思路;

一、自旋锁简介

通常情况下解决多线程共享资源逻辑一致性问题有两种方式:

  • 互斥锁:当发现资源被占用的时候,会阻塞自己直到资源解除占用,然后再次尝试获取;
  • 自旋锁:当发现占用时,一直尝试获取锁(线程没有被挂起的过程,也就没有线程调度切换的消耗);

对于这两种方式没有优劣之分,只有是否适合当前的场景;具体的对比就不在继续深入了,如果你很感兴趣可以查看 《多处理器编程的艺术》 提取码:rznn ;

但是如果竞争非常激烈的时候,使用自旋锁就会产生一些额外的问题:

  • 可能导致一些线程始终无法获取锁(争抢的时候必然是当前活跃线程获得锁的几率大),也就是饥饿现象;
  • 因为自旋锁会依赖一个共享的锁标识,所以竞争激烈的时候,锁标识的同步也需要消耗大量的资源;
  • 如果要用自旋锁实现公平锁(即先到先获取),此时就还需要额外的变量,也会比较麻烦;

解决这些问题其中的一种办法就是使用队列锁,简单来讲就是让这些线程排队获取;下面我们介绍常用的两种,即 CLH 锁MCS 锁

二、CLH 锁

CLH 是 Craig、Landin 和 Hagersten 三位作者的缩写,具体内容在 《Building FIFO and Priority-Queuing Spin Locks from Atomic Swap》 论文中有详细介绍,大家可以自行查看;我们 JDK 中 java.util.concurrent.locks.AbstractQueuedSynchronizer 就是根据 CLH 锁的变种实现的;

简单实现:

public class CLH implements Lock {
  private final ThreadLocal<Node> preNode = ThreadLocal.withInitial(() -> null);
  private final ThreadLocal<Node> node = ThreadLocal.withInitial(Node::new);
  private final AtomicReference<Node> tail = new AtomicReference<>(new Node());

  private static class Node {
    private volatile boolean locked;
  }

  @Override
  public void lock() {
    final Node node = this.node.get();
    node.locked = true;
    Node pre = this.tail.getAndSet(node);
    this.preNode.set(pre);
    while (pre.locked) ;
  }

  @Override
  public void unlock() {
    final Node node = this.node.get();
    node.locked = false;
    this.node.set(this.preNode.get());
  }
}


clh

三、MCS 锁

同样 MCS 是 John M. Mellor-Crummey 和 Michael L. Scott 名字的缩写,具体内容可以在 《Algorithms for Scalable Synchronization on Shared-Memory Multiprocessors》 论文中查看;

简单实现:

public class MCS implements Lock {
  private final ThreadLocal<Node> node = ThreadLocal.withInitial(Node::new);
  private final AtomicReference<Node> tail = new AtomicReference<>();

  private static class Node {
    private volatile boolean locked = false;
    private volatile Node next = null;
  }

  @Override
  public void lock() {
    Node node = this.node.get();
    node.locked = true;
    Node pre = tail.getAndSet(node);
    if (pre != null) {
      pre.next = node;
      while (node.locked) ;
    }
  }

  @Override
  public void unlock() {
    Node node = this.node.get();
    if (node.next == null) {
      if (tail.compareAndSet(node, null)) {
        return;
      }
      while (node.next == null) ;
    }
    node.next.locked = false;
    node.next = null;
  }
}


clh

总结

  • 以上的代码我已经测试过,大家可以直接拿下来自行实验;
  • CLH 锁和 MCS 锁区别主要有两点:1. 链表结构的区别;2. 自旋对象的区别,CLH 是在前驱节点上自旋,而 MCS 是在自身节点上自旋;这里第二点才是最重要的,主要体现在 SMP(Symmetric Multi-Processor)NUMA(Non-Uniform Memory Access) 不同的处理器架构上;这里大家可以自行 Google;
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/167689.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • kalilinux更换源(pip3换源)

    文件位置:/etc/apt/soucrce.list注释kali原来的源,下面添加任意一个源即可#kali官方源debhttp://http.kali.org/motomainnon-freecontribdeb-srchttp://http.kali.org/motomainnon-freecontribdebhttp://security.kali.org/moto/updatesmaincontribnon-freedeb-srchttp://

  • 孤儿进程与僵尸进程[总结]「建议收藏」

    孤儿进程与僵尸进程[总结]「建议收藏」1、前言之前在看《unix环境高级编程》第八章进程时候,提到孤儿进程和僵尸进程,一直对这两个概念比较模糊。今天被人问到什么是孤儿进程和僵尸进程,会带来什么问题,怎么解决,我只停留在概念上面,没有深入

  • ssh学习整理笔记[通俗易懂]

    ssh学习整理笔记[通俗易懂]ssh1、ssh简介ssh(安全外壳协议)ssh为secureshell的缩写,ssh为建立在应用层和传输层基础上的安全协议 ssh端口ssh端口:22Linux中守护进程:sshd安装服务:OpenSSH服务端主程序:/usr/sbin/sshd客户端主程序:/usr/bin/ssh 相关文件服务端配置文件:/etc/ssh/sshd_c

  • MySQL与php时间戳与日期格式的相互转换[通俗易懂]

    MySQL与php时间戳与日期格式的相互转换[通俗易懂]文章来自:源码在线https://www.shengli.me/php/336.html  

  • Vim详细配置_mini5配置

    Vim详细配置_mini5配置Vim配置要点一、在终端中开打.vimrc文档二、在.vimrc文档中添加配置内容1.常用设置2.自动备份3.自动补全三、保存退出四、代码高亮不显示一、在终端中开打.vimrc文档vi~/.vimrc二、在.vimrc文档中添加配置内容1.常用设置setnumber “显示行号syntaxon “语法高亮度显示setautoindent “vim使用自动对起,也就是把当前行的对起格式应用到下一行setsmartindent “依据上面的对起格式,智能的

  • Android进阶之AlertDialog自定义

    AlertDialog的自定义方式有很多种,这里介绍两种。 第一种是比较简单的,只自定义内容。在AlertDialog使用详解中,非常详细的介绍了以下六种使用方法。一、简单的AlertDialog(只显示一段简单的信息,比如about us)二、带按钮的AlertDialog(显示提示信息,让用户操作,比如exit时的警告框)三、类似ListView的AlertDialog(展示内容,比如某人的一

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号