1.1 悲观锁和乐观锁
悲观锁:还是像它的名字一样,对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管三七二十一,直接上了锁就操作资源了。

乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较-替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。
1.2 两种常见的锁
Synchronized 互斥锁(悲观锁,有罪假设)

采用synchronized修饰符实现的同步机制叫做互斥锁机制,它所获得的锁叫做互斥锁。每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。任何一个对象系统都会为其创建一个互斥锁,这个锁是为了分配给线程的,防止打断原子操作。每个对象的锁只能分配给一个线程,因此叫做互斥锁。

ReentrantReadWriteLock 读写锁(乐观锁,无罪假设)

ReentrantLock是排他锁,排他锁在同一时刻仅有一个线程可以进行访问,实际上独占锁是一种相对比较保守的锁策略,在这种情况下任何“读/读”、“读/写”、“写/写”操作都不能同时发生,这在一定程度上降低了吞吐量。然而读操作之间不存在数据竞争问题,如果”读/读”操作能够以共享锁的方式进行,那会进一步提升性能。因此引入了ReentrantReadWriteLock,顾名思义,ReentrantReadWriteLock是Reentrant(可重入)Read(读)Write(写)Lock(锁),我们下面称它为读写锁。

读写锁内部又分为读锁和写锁,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。读锁和写锁分离从而提升程序性能,读写锁主要应用于读多写少的场景。
ReentrantLock常常对比着synchronized来分析,我们先对比着来看然后再一点一点分析。

(1)synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。

(2)synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。

(3)synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。

ReentrantLock好像比synchronized关键字没好太多,我们再去看看synchronized所没有的,一个最主要的就是ReentrantLock还可以实现公平锁机制。什么叫公平锁呢?也就是在锁上等待时间最长的线程将获得锁的使用权。通俗的理解就是谁排队时间最长谁先执行获取锁。
4 sychronized修饰的方法或者语句块在代码执行完之后锁会自动释放,而是用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内!

与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程)从理论上讲,与互斥锁定相比,使用读-写锁允许的并发性增强将带来更大的性能提高。
什么是可重入锁?

ReentrantLock是可重入锁,什么是可重入锁呢?可重入锁就是当前持有该锁的线程能够多次获取该锁,无需等待。可重入锁是如何实现的呢?这要从ReentrantLock的一个内部类Sync的父类说起,Sync的父类是AbstractQueuedSynchronizer(后面简称AQS)。
什么是AQS?

AQS是JDK1.5提供的一个基于FIFO等待队列实现的一个用于实现同步器的基础框架,这个基础框架的重要性可以这么说,JCU包里面几乎所有的有关锁、多线程并发以及线程同步器等重要组件的实现都是基于AQS这个框架。AQS的核心思想是基于volatile int state这样的一个属性同时配合Unsafe工具对其原子性的操作来实现对当前锁的状态进行修改。当state的值为0的时候,标识改Lock不被任何线程所占有。

ReentrantLock锁的架构

ReentrantLoc的架构相对简单,主要包括一个Sync的内部抽象类以及Sync抽象类的两个实现类。上面已经说过了Sync继承自AQS,他们的结构示意图如下:

上图除了AQS之外,我把AQS的父类AbstractOwnableSynchronizer(后面简称AOS)也画了进来,可以稍微提一下,AOS主要提供一个exclusiveOwnerThread属性,用于关联当前持有该所的线程。另外、Sync的两个实现类分别是NonfairSync和FairSync,由名字大概可以猜到,一个是用于实现公平锁、一个是用于实现非公平锁。那么Sync为什么要被设计成内部类呢?我们可以看看AQS主要提供了哪些protect的方法用于修改state的状态,我们发现Sync被设计成为安全的外部不可访问的内部类。ReentrantLock中所有涉及对AQS的访问都要经过Sync,其实,Sync被设计成为内部类主要是为了安全性考虑,这也是作者在AQS的comments上强调的一点
AQS的等待队列

作为AQS的核心实现的一部分,举个例子来描述一下这个队列长什么样子,我们假设目前有三个线程Thread1、Thread2、Thread3同时去竞争锁,如果结果是Thread1获取了锁,Thread2和Thread3进入了等待队列,那么他们的样子如下:

AQS的等待队列基于一个双向链表实现的,HEAD节点不关联线程,后面两个节点分别关联Thread2和Thread3,他们将会按照先后顺序被串联在这个队列上。这个时候如果后面再有线程进来的话将会被当做队列的TAIL。

1)入队列

我们来看看,当这三个线程同时去竞争锁的时候发生了什么?

代码:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
解读:

三个线程同时进来,他们会首先会通过CAS去修改state的状态,如果修改成功,那么竞争成功,因此这个时候三个线程只有一个CAS成功,其他两个线程失败,也就是tryAcquire返回false。

接下来,addWaiter会把将当前线程关联的EXCLUSIVE类型的节点入队列:

代码:private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
解读:

如果队尾节点不为null,则说明队列中已经有线程在等待了,那么直接入队尾。对于我们举的例子,这边的逻辑应该是走enq,也就是开始队尾是null,其实这个时候整个队列都是null的。

代码:

private Node enq(final Node node) {
for (;

ReentrantLock原理及AQS(羊群效应+实操)相关推荐

  1. OpenFlow协议原理及基本配置-网络测试仪实操

    一.OpenFlow协议原理 1.OpenFlow技术背景 ●转发和控制分离是SDN网络的本质特点之一.在SDN网络架构中,控制平面与转发平面分离,网络的管理和状态在逻辑上集中到一起,底层的网络基础从 ...

  2. 【mysql进阶】MTS主从同步原理及实操指南(七)

    0.引言 随着业务场景的深入和请求量的剧增,单库实现读写越来越趋近瓶颈,于是我们想到搭建主从库,主库负责写,从库负责读,从而实现读写分离,提高查询效率. 但是主从库之间的数据如何同步呢?很明显我们写入 ...

  3. reentrantlock原理_你必须要知道的热门 ReentrantLock 及 AQS 的实现原理

    码农每日一题长按关注,工作日每天分享一个技术知识点. 提到 JAVA 加锁,我们通常会想到 synchronized 关键字或者是 Java Concurrent Util(后面简称JCU)包下面的 ...

  4. 从原理到实操,看当前最佳的YOLO V4是如何炼成的?

    YOLO系列的网络都有一个共同的特点,即追求网络精度也追求网络速度,YOLO V4在此基础上又多了一个追求,那就是降低硬件要求. YOLO V4 的开发历程很有意思,其中评估.修改和整合了很多有趣的新 ...

  5. MySQL主从复制原理(原理+实操)

    1.MySQL主从复制原理(原理+实操) 主从复制简介 在实际的生产中,为了解决Mysql的单点故障已经提高MySQL的整体服务性能,一般都会采用「主从复制」. 比如:在复杂的业务系统中,有一句sql ...

  6. git原理详解与实操指南_基于dockercompose的Gitlab CI/CD实践amp;排坑指南

    长话短说 经过长时间实操验证,终于完成基于Gitlab的CI/CD实践,本次实践的坑位很多, 实操过程尽量接近最佳实践(不做hack, 不做骚操作),记录下来加深理解. 看过博客园<docker ...

  7. 异地局域网对接:异地组网原理与实操

    无论是在工作还是学习过程中,我们经常会有异地访问局域网的需求.所谓异地组网,就是要打通两地的局域网络环境,在任何一地的局域网络环境下,能够通过输入异地局域网地址的方式,实现类似于本地局域网访问的效果. ...

  8. redis cluster 集群 HA 原理和实操(史上最全、面试必备)

    文章很长,建议收藏起来慢慢读!疯狂创客圈总目录 语雀版 | 总目录 码云版| 总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 经典图书:<Java高并发核心编程(卷1)> 面试必备 ...

  9. 三坐标测头标定原理和标准球实操

    测头标定 三坐标测量机测头标定是测量过程中非常重要的一个环节,在标定过程中产生的误差将影响最终的测量结果,特别是标定不同位置.角度和长度的测头时,标定结果的准确性显得尤其重要.因为在后续的测量过程中如 ...

  10. RFM模型原理详解与实操运用

    RFM模型原理详解与实操运用 RFM模型原理介绍 为什么要使用RFM模型 RMF模型原理介绍 RFM模型用户细分 RFM模型实例操作 背景/数据介绍 RFM模型异化构建 代码实现 最近在 运营课程中学 ...

最新文章

  1. 是凡尔赛本赛了,马斯克回应成为全球首富
  2. 检查是否已使用jQuery选中复选框
  3. tdi_fw贴码析(TDI开源网络防火墙分析)
  4. php自动断词,PHP自动分页、防止英文单词被截段、去除HTML代码
  5. 一口气拿了9家公司的offer,年薪50W
  6. intellij中springboot热部署
  7. 在tomcat上部署项目,实现类似添加这样的功能之后,tomcat要运行很久,解决办法
  8. 28 Java类的加载机制、什么是类的加载、类的生命周期、加载:查找并加载类的二进制数据、连接、初始化、类加载器、双亲委派模型、自定义类加载器
  9. apache 静态编译和动态编译参考
  10. 文件目录管理与显示c语言,Centos 7 文件和目录管理
  11. g++默认参数_C ++默认参数| 查找输出程序| 套装2
  12. 8 个Python技巧 每天工作效率高一点 升职快人一步
  13. stl:空间配置器的标准接口
  14. 【运输量预测】基于matlab多种算法公路运输量预测【含Matlab源码 041期】
  15. 正确的座机号码格式_正确的填写手机号码的格式是什么?
  16. 河南省第十届ACM程序设计大赛参赛心得
  17. 复现论文常用函数(一)tf.one_hot,tf.train.batch,tf.train.shuffle_batch,数据读取机制,获取文件路径,Bunch等
  18. 清理工作区git clean -fd
  19. Git Github 学习
  20. http status状态码,readyState状态码

热门文章

  1. java-php-python-ssm演唱会购票系统计算机毕业设计
  2. 基于Java Socket的局域网聊天系统
  3. 【Typora启动报错】This beta version of Typora is expired, please download and install a newer version.
  4. sql拼接同一字段_sql多个字段拼接
  5. 计算机显示桌面图标不见了,电脑显示器桌面图标不见了怎么办
  6. [leetcode 3sum】 三数之和问题 @python
  7. 硬盘数据恢复方法有哪些?希望这些方法能帮助你
  8. 2021年茶艺师(初级)报名考试及茶艺师(初级)模拟考试题库
  9. flashfxp怎么传文件,小编教你flashfxp怎么传文件
  10. ubuntu20.04 常用开发工具整理