Jdk1.6 JUC源码解析(7)-locks-ReentrantLock
作者:大飞
功能简介:
- Java代码层面提供的锁机制,可做为Synchronized(jvm内置)的替代物,和Synchronized一样都是可重入的。
- 与Synchronized相比较而言,ReentrantLock有以下优势:支持公平/非公平锁、支持可中断的锁、支持非阻塞的tryLock(可超时)、支持锁条件、可跨代码块使用(一个地方加锁,另一个地方解锁),总之比Synchronized更加灵活。但也有缺点,比如锁需要显示解锁、无法充分享用JVM内部性能提升带来的好处等等。
源码分析:
- ReentrantLock实现了Lock接口,先来看下这个接口:
public interface Lock { /** * 获取锁,如果锁无法获取,当前线程被阻塞,直到锁可以获取并获取成功为止。 */ void lock(); /** * 在当前线程没有被中断的情况下获取锁。 * * 如果获取成功,方法结束。 * * 如果锁无法获取,当前线程被阻塞,直到下面情况发生: * * 1.当前线程(被唤醒后)成功获取锁。 * 2.当前线程被其他线程中断。 */ void lockInterruptibly() throws InterruptedException; /** * 如果当前锁是可用的,获取锁。 * * 获取成功后,返回true。 * 如果当前锁不可用,返回false。 */ boolean tryLock(); /** * 如果锁在给定超时时间内可用,并且当前线程没有被中断,那么获取锁。 * * 如果锁可用,获取锁成功并返回true。 * * 如果锁无法获取,当前线程被阻塞,直到下面情况发生: * * 1.当前线程(被唤醒后)成功获取锁。 * 2.当前线程被其他线程中断。 * 3.指定的等待时间超时。 */ boolean tryLock(long time, TimeUnit unit) throws InterruptedException; /** * 释放锁。 */ void unlock(); /** * 返回一个和当前锁实例相关联的条件。 * * 当前线程必须首先获取锁后才能在锁条件上等待。 * 一个Condition的await()方法调用会在等待之前自动释放锁,在等待结束 * 前重新获取锁。 */ Condition newCondition(); }
- 之前分析AQS的时候提到过,基于AQS构建的同步机制都会使用内部帮助类继承AQS的方式构建,看下ReentrantLock中的同步机制:
//内部同步机制的引用。 private final Sync sync; /** * 这个锁实现的基本同步控制机制,下面会提供公平和非公平版本的子类。 * 利用AQS的state来表示锁持有(重入)的次数。. */ static abstract class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; /** * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */ abstract void lock(); /** * 方法用来支持非公平的tryLock */ final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { //如果当前没有任何线程获取锁(锁可用),尝试设置state。 if (compareAndSetState(0, acquires)) { //如果设置成功,将当前线程信息设置到AQS中(所有权关联)。 setExclusiveOwnerThread(current); return true; } } //如果锁已经被持有,那么判断一下持有锁的线程是否为当前线程。 else if (current == getExclusiveOwnerThread()) { //如果是当前线程在持有锁,那么这里累计一下重入次数。 int nextc = c + acquires; if (nextc < 0) // overflow 重入次数最大不能超过int的最大值 throw new Error("Maximum lock count exceeded"); //设置到AQS的state中 setState(nextc); return true; } //如果锁已经被持有,且持有线程不是当前线程,返回false。 return false; } protected final boolean tryRelease(int releases) { //释放时,这里要减去重入次数。 int c = getState() - releases; //判断控制权关系是否正确。 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { //如果当前线程完全释放了锁(重入次数为0) free = true; //解除所有权关系。 setExclusiveOwnerThread(null); } //设置重入次数。 setState(c); //返回是否释放成功(或者说是否完全释放)。 return free; } protected final boolean isHeldExclusively() { // While we must in general read state before owner, // we don't need to do so to check if current thread is owner return getExclusiveOwnerThread() == Thread.currentThread(); } final ConditionObject newCondition() { return new ConditionObject(); } // Methods relayed from outer class final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); } final int getHoldCount() { return isHeldExclusively() ? getState() : 0; } final boolean isLocked() { return getState() != 0; } /** * Reconstitutes this lock instance from a stream. * @param s the stream */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); setState(0); // reset to unlocked state } }
接下来先看一下非公平版本的子类:
/** * Sync object for non-fair locks */ final static class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { //这里首先尝试一个短代码路径,直接CAS设置state,尝试获取锁。 //相当于一个插队的动作(可能出现AQS等待队列里有线程在等待,但当前线程竞争成功)。 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1);//如果CAS失败,调用AQS的独占请求方法。 } protected final boolean tryAcquire(int acquires) { //调用上面父类的nonfairTryAcquire方法。 return nonfairTryAcquire(acquires); } }
再来先看一下公平版本的子类:
/** * Sync object for fair locks */ final static class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } /** * 公平版本的tryAcquire。 * 只有在递归(重入)或者同步队列中没有其他线程 * 或者当前线程是等待队列中的第一个线程时才准许访问。 */ protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { //如果当前锁可用,且同步等待队列中没有其他线程,那么尝试设置state setExclusiveOwnerThread(current); //如果设置成功,相当于获取锁成功,设置所有权关系。 return true; } } else if (current == getExclusiveOwnerThread()) { //如果当前线程已经持有该锁,那么累计重入次数。 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }
小总结一下:
非公平版的锁-加锁操作
1.当前线程首先会无条件的执行一个CAS操作来获取锁,如果CAS操作成功,获取锁成功。
2.如果第1步没成功,当前会检查锁是否被其他线程持有,也就是锁是否可用。
3.如果没有其他线程持有锁,会以CAS的方式尝试获取锁,如果CAS操作成功,获取锁成功。
4.如果有其他线程持有锁,会判断一下持有锁的线程是否为当前线程,如果是当前线程,重入次数+1,获取锁成功。
5.根据AQS的分析,上述2、3、4步会执行多次,如果最终获取锁失败,当前线程会被阻塞,等待其他线程执行解锁操作将其唤醒。
公平版的锁-加锁操作
1.当前线程首先会检查锁是否被其他线程持有,并且当前同步等待队列里有没有其他线程在等待。
2.如果没有其他线程持有锁,且同步等待队列里没有其他线程,会以CAS的方式尝试获取锁,如果CAS操作成功,获取锁成功。
3.如果有其他线程持有锁,会判断一下持有锁的线程是否为当前线程,如果是当前线程,重入次数+1,获取锁成功。
4.根据AQS的分析,上述1、2、3步会执行多次,如果最终获取锁失败,当前线程会被阻塞,等待其他线程执行解锁操作将其唤醒。
非公平版和公平版锁的解锁操作一样
1.当前线程首先将锁重入次数减1(AQS的state),如果减1后结果为0,将当前同步器的线程信息置空,并唤醒同步等待队列中队头的等待线程。
2.如果第1步中,重入次数减1后结果不为0(说明当前线程还持有当前锁),方法结束。
- 有了内部的基础同步机制,ReentrantLock的实现就很简单了,直接看代码:
/** * 默认情况下构建非公平锁。 */ public ReentrantLock() { sync = new NonfairSync(); } /** * 根据给定的公平策略生成相应的实例。 * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = (fair)? new FairSync() : new NonfairSync(); } public void lock() { sync.lock(); } public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } public boolean tryLock() { return sync.nonfairTryAcquire(1); } public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } public void unlock() { sync.release(1); } public Condition newCondition() { return sync.newCondition(); }
最后看一下一些支持监测的方法:
/** * 获取当前线程的对当前锁的持有(重入)次数。 */ public int getHoldCount() { return sync.getHoldCount(); } /** * 判断当前锁是否被当前线程持有。 */ public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); } /** * 判断当前锁是否被(某个线程)持有。 */ public boolean isLocked() { return sync.isLocked(); } /** * 当前锁是否为公平锁。 */ public final boolean isFair() { return sync instanceof FairSync; } /** * 获取持有当前锁的线程。 */ protected Thread getOwner() { return sync.getOwner(); } /** * 判断是否有线程在当前锁的同步等待队列中等待。 */ public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } /** * 判断给定的线程是否在当前锁的同步等待队列中等待。 */ public final boolean hasQueuedThread(Thread thread) { return sync.isQueued(thread); } /** * 获取当前锁的同步等待队列中的等待线程(估计)数量。 */ public final int getQueueLength() { return sync.getQueueLength(); } /** * 获取当前锁的同步等待队列中等待的线程。 */ protected Collection<Thread> getQueuedThreads() { return sync.getQueuedThreads(); } /** * 判断是否有线程在给定条件的条件等待队列上等待。 */ public boolean hasWaiters(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition); } /** * 获取给定条件的条件等待队列中等待线程的(估计)数量。 */ public int getWaitQueueLength(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition); } /** * 获取给定条件的条件等待队列中等待线程。 */ protected Collection<Thread> getWaitingThreads(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition); }
ReentrantLock的代码解析完毕!
参见:Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer
相关推荐
后端开发是一个涉及广泛技术和工具的领域,这些资源对于构建健壮、可扩展和高效的Web应用程序至关重要。以下是对后端开发资源的简要介绍: 首先,掌握一门或多门编程语言是后端开发的基础。Java、Python和Node.js是其中最受欢迎的几种。Java以其跨平台性和丰富的库而著名,Python则因其简洁的语法和广泛的应用领域而备受欢迎。Node.js则通过其基于JavaScript的单线程异步I/O模型,为Web开发提供了高性能的解决方案。 其次,数据库技术是后端开发中不可或缺的一部分。关系型数据库(如MySQL、PostgreSQL)和非关系型数据库(如MongoDB、Redis)各有其特点和应用场景。关系型数据库适合存储结构化数据,而非关系型数据库则更适合处理大量非结构化数据。 此外,Web开发框架也是后端开发的重要资源。例如,Express是一个基于Node.js的Web应用开发框架,它提供了丰富的API和中间件支持,使得开发人员能够快速地构建Web应用程序。Django则是一个用Python编写的Web应用框架,它采用了MVC的软件设计模式,使得代码结构更加清晰和易于维护。
华为数字化转型实践28个精华问答glkm.pptx
新员工入职培训全流程资料包(100+个文件) 1入职流程指引 万科新职员入职通知书 万科新职员入职引导手册 新进员工跟进管理表 新员工入职报到工作单(文职) 新员工入职报到流程 新员工入职流程表 新员工入职手续办理流程(工厂 新员工入职手续清单 新员工入职须知 新员工入职训流程 新员工入职引导表(导师用) 2 入职工具表格 3 培训方案计划 4培训管理流程 5培训教材课件 6 培训效果检测 7 员工管理制度 8 劳动合同协议 9 新员工培训PPT模板(28套)
FX5U PLC作为主、从站的通讯方式程序实例,以及包含详细说明文件...
技术需求报告-集行波测距与故障录波功能于一体的电网综合故障分析系统.docx
最新二开版本源码博客论坛源码,UI很漂亮,可切换皮肤界面.rar最新二开版本源码博客论坛源码,UI很漂亮,可切换皮肤界面.rar
2024-2030全球及中国广谱防晒霜行业研究及十五五规划分析报告
基于Qt Creator实现中国象棋人机对战, c++实现.zip
华为用“三阶段十二步”法保证业务战略引领数字化转型glkx.pptx
基于matlab实现自适应稳健波束形成对角加载算法,其与输入信噪比的关系.rar
热塑性弹性体,全球前21强生产商排名及市场份额
详见:https://blog.csdn.net/Timi2019/article/details/138357258 【项目技术】 python+Django+mysql 【实现功能】 网站前台: (1)用户可以在不登录的情况下访问本系统,但是不能进行数据的分析,也不能对自己的个人信息进行修改。 (2)用户的注册与登录:游客想要在一个网站对自己的信息进行修改的话,需要经过一系列的有验证信息的注册,成为网站的正式用户后,可以编辑或修改自己的个人信息。 (3)评论分析:用户可以在网站内对所有的评论进行查看和分析。 (4)个人信息:通过个人信息查看功能可以查看自己的个人信息,还可以对密码进行修改。 (5)系统简介:用户可以在网站的首页上查看系统的信息,如用户数量、新闻数量、评价数量等信息。 网站后台: (6)用户信息管理:管理员可以查看和维护网站内所有的用户信息,可以通过用户的编号或者用户名进行查找,查找到具体的用户后可以对用户的信息进行修改,也可以直接删除用户的信息。 (7)新闻管理模块:后台管理员可以对网站内的新闻信息进行管理
某知名大型集团信息化项目建设方案qy.pptx
基于matlab实现潮流计算程序,MATLAB潮流计算程序.rar
搭建数据分析和机器学习平台,实现如下功能:设计一个网页版的用户界面,支持从本地选取数据集、自动可视化分析、查看训练记录、查看训练模型参数和绘图和支持完成机器学习任务。核心工具:streamlit和pycaret,部署和运行方便,只需streamlit run main.py命令即可。
一种统一的单隐层的前馈网络(SLFNs)的在线序列学习算法。该算法被称为在线序极端学习机器(OS-ELM),可以学习固定或不同块大小的逐块或逐块数据)。OS-ELM中加性节点的激活函数可以是任意有界的不变分段连续函数,RBF节点的激活函数可以是任意可积的分段连续函数。在OS-ELM中,随机选择隐藏节点的参数(加性节点的输入权值和偏差或RBF节点的中心和影响因子),并根据顺序到达的数据解析确定输出权值。该算法采用了Huang等人开发的批处理学习的思想,该思想已被证明比其他批处理训练方法非常快。除了选择隐藏节点的数量外,还不需要手动选择其他控制参数。
仿我图网素材购买素材下载素材交易平台网站源码.rar仿我图网素材购买素材下载素材交易平台网站源码.rar
python tkinter
2024-2030全球与中国伊维菌素片剂市场现状及未来发展趋势
Python数据分析大作业(ARIMA 自回归积分滑动平均模型) 4000+字 图文分析文档 销售价格&库存分析+完整python代码