ReentrantLock介绍

ReentrantLock 基于AQS实现了公平和非公平的独占锁功能。

ReentrantLock定义AQS的同步状态(synchronization state)如下:

State为0表示锁可用;为1表示被占用;为N表示锁重入的次数,是独占资源。

ReentrantLock实现公平锁原理

案例代码如下:

1、启动文件

public class Main {public static void main(String[] args) throws ParseException {ReentrantLock lock = new ReentrantLock(true);Thread t1 = new Thread(new Task(lock),"Thread-1");Thread t2 = new Thread(new Task(lock),"Thread-2");Thread t3 = new Thread(new Task(lock),"Thread-3");t1.start();t2.start();t3.start();}
}

2、Task

import java.util.concurrent.locks.ReentrantLock;public class Task implements Runnable{private ReentrantLock lock;public Task(ReentrantLock lock) {this.lock = lock;}@Overridepublic void run() {try {lock.lock();System.out.println(Thread.currentThread().getName() + "获取到锁....");Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println(Thread.currentThread().getName() + "释放锁....");lock.unlock();}}
}

3、执行结果:

案例分析

1、Thread-1调用lock方法

ReentrantLock内部继承AQS实现了1个抽象类Sync

继承于Sync实现了2个内部类FairSync(公平的)和NonfairSync(非公平的)

此时调用FairSync的lock方法

acquire方法来自AQS,注意参数是1

tryAcquire是ReentrantLock自己实现的,尝试获取锁

protected final boolean tryAcquire(int acquires) { //参数是1final Thread current = Thread.currentThread(); //当前线程int c = getState(); //获取当前同步状态if (c == 0) { //如果是0,则锁没有被占用//等待队列中,前面没有其他等待的线程,则用CAS的方法更新同步状态stateif (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current); //成功的话,则设置锁的占有线程为当前线程return true; //返回获取资源成功}}//如果锁已经被占用,则判断是不是自己占用的else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;//如果是自己占用的,则是重入,增加state值,累加1if (nextc < 0) //重入次数过大,抛出异常throw new Error("Maximum lock count exceeded");setState(nextc); //设置state值return true; //重入返回ture}return false;//没有获取资源返回false}

Thread-1获取了锁资源,没有释放。

2、Thread-2,开始请求资源,调用lock,此时锁资源还被Thread-1占用

addWaiter方法:

private Node addWaiter(Node mode) {//把当前线程包装成节点,准备放入等待队列Node node = new Node(Thread.currentThread(), mode);// Try the fast path of enq; backup to full enq on failure//尝试直接把节点设置成队尾,否则执行enqNode pred = tail;if (pred != null) {node.prev = pred;//当前节点的上一个节点是之前的队尾节点if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}//当前节点插入队尾enq(node);return node;}

enq自旋+初始化等待队列,并返回Thread-2节点

private Node enq(final Node node) {//采用自旋,保证节点插入for (;;) {Node t = tail;if (t == null) { // Must initialize 如果队列为空,则创建一个空的节点,设置为头尾节点if (compareAndSetHead(new Node()))tail = head;} else {node.prev = t;if (compareAndSetTail(t, node)) { //队列不为空,追加到队尾t.next = node;return t;}}}}

然后对Thread-2包装节点执行acquireQueued

final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {//判断节点的前任节点是不是头节点,头节点是一个空节点final Node p = node.predecessor();//如果是头节点,则说明当前节点是队列里的第一个节点,首节点。//则尝试获取锁资源,此处因为Thread-1占用着资源,则失败if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;return interrupted;}//失败之后,则判断当前节点线程Thread-2是不是可以阻塞if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}}

是否阻塞shouldParkAfterFailedAcquire

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;//前驱节点的状态if (ws == Node.SIGNAL) //如果是SIGNAL,则说明前驱节点状态可以唤醒后继节点,可以阻塞/** This node has already set status asking a release* to signal it, so it can safely park.*/return true;if (ws > 0) {/** Predecessor was cancelled. Skip over predecessors and* indicate retry.*/do {node.prev = pred = pred.prev; //只有CANCELLED状态大于0,则把取消状态的节点从队列删除} while (pred.waitStatus > 0);pred.next = node;} else {/** waitStatus must be 0 or PROPAGATE.  Indicate that we* need a signal, but don't park yet.  Caller will need to* retry to make sure it cannot acquire before parking.*/compareAndSetWaitStatus(pred, ws, Node.SIGNAL);//设置前驱节点为SIGNAL状态}return false;}

如果可以阻塞,则调用parkAndCheckInterrupt,阻塞线程,至此Thread-2进入队列,并阻塞了,耐心等待

3、Thread-3同Thread-2,略过

4、Thread-1释放锁资源

release方法:

tryRelease

然后通过unparkSuccessor,唤醒首节点,保证公平策略。

至此Thread-1释放完成,Thread-2可以获得资源,依次类推。

转载于:https://blog.51cto.com/janephp/2408688

多线程(十、AQS原理-ReentrantLock公平锁)相关推荐

  1. ReentrantLock 公平锁和非公平锁加锁和解锁源码分析(简述)

    - title: ReentrantLock 公平锁和非公平锁加锁和解锁源码分析(简述) - date: 2021/8/16 文章目录 一.ReentrantLock 1. 构造函数 二.Reentr ...

  2. 图解ReentrantLock公平锁和非公平锁实现

    概述 ReentrantLock是Java并发中十分常用的一个类,具备类似synchronized锁的作用.但是相比synchronized, 它具备更强的能力,同时支持公平锁和非公平锁. 公平锁: ...

  3. ReentrantLock 实现原理(公平锁和非公平锁)

    使用 synchronized 来做同步处理时,锁的获取和释放都是隐式的,实现的原理是通过编译后加上不同的机器指令来实现. 而 ReentrantLock 就是一个普通的类,它是基于 AQS(Abst ...

  4. 深入分析ReentrantLock公平锁和非公平锁的区别 (转)

    在ReentrantLock中包含了公平锁和非公平锁两种锁,通过查看源码可以看到这两种锁都是继承自Sync,而Sync又继承自AbstractQueuedSynchronizer,而AbstractQ ...

  5. 深入剖析ReentrantLock公平锁与非公平锁源码实现

    原文地址: https://blog.csdn.net/lsgqjh/article/details/63685058 ReentrantLock是JUC包中重要的并发工具之一,支持中断和超时.还支持 ...

  6. 深入分析ReentrantLock公平锁和非公平锁的区别

    在ReentrantLock中包含了公平锁和非公平锁两种锁,通过查看源码可以看到这两种锁都是继承自Sync,而Sync又继承自AbstractQueuedSynchronizer,而AbstractQ ...

  7. AQS中的公平锁和非公平锁

    正文 公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁. 优点:所有的线程都能得到资源,不会饿死在队列中. 缺点:吞吐量会下降很多,队列里面除了第一个 ...

  8. 明锁,reentrantlock公平锁与非公平锁,互斥与通信,awit与signal使用

    lock又叫明锁,他是一个类.而synchronized是在jvm层面的 lock loc1=new reentrantlock():lock默认是非公平锁lock loc2 = new reentr ...

  9. java中ReentrantLock实现,公平锁和非公平锁,AQS并发队列,

    一般在java中,遇到并发的时候,我们很多时候可能会使用synchronized关键字来实现锁,但是synchronized关键字有一定的缺陷(比如无法实现类似读锁.非公平),而Lock可以实现.在j ...

最新文章

  1. Spring-Security (学习记录四)--配置权限过滤器,采用数据库方式获取权限
  2. 问题战略[置顶] 十八年开发经验分享(四)问题解决篇(下)
  3. java 判断两个数是否异号
  4. ASIO 腾空出世 (那些年我们追过的网络库.PartII)
  5. 这种事情干不得!微信已动手处理4.5万个公众号
  6. Spring Boot 在Gradle构建中使用Log4j日志
  7. @Transactional之Spring事务深入理解
  8. MySQL 第六次练习(索引)
  9. 获取以及自定义User-Agent在URLSession, NSURLConnection, WKWebView iOS
  10. c#中panel控件有什么作用
  11. MySQL InnoDB引擎如何保证事务特性
  12. Nutch抓取过程简析
  13. 路由器工作原理与静态路由配置
  14. 微软解决Office兼容性问题 补丁包 转帖之www.it.com.cn
  15. PHP生成订单号算法
  16. Java EE|File类的常用方法和InputStream, OutputStream 的用法
  17. 网易互娱2017实习生招聘在线笔试(三)
  18. Qt5.9/C++架构实例(一个简单的MCV架构应用实例)
  19. uniapp 蓝牙连接测温仪
  20. 蒙特梭利素材-【数字海报】蒙氏素材

热门文章

  1. GAN不只会造假:捕获数据中额外显著特征,提高表征学习可解释性,效果超越InfoGAN | IJCAI 2020...
  2. NeurIPS 2019放榜:华人作者贡献42%,谷歌170篇屠榜;国内清华第一,腾讯领衔产业界...
  3. 对话G7 CEO翟学魂:收入30亿仍亏损是为研发,上市不是目前主要考虑的事情
  4. 反向区域DNS解析服务
  5. python全栈开发笔记--------条件语句
  6. 1 创建一个存储过程,以及对存储过程的调用 MySQL
  7. MySQL多源复制【转】
  8. java之 ------ 可变參数和卫条件
  9. java-Java可以对方法进行序列化?
  10. ubuntu 上code blocks 建glut工程时问题