ReentrantLock-源码解析
ReentrantLock类注释
1、可重入互斥锁,意思是表示该锁能够支持一个线程对资源的重复加锁,该锁还支持获取锁的公平和非公平性选择。synchronized关键字隐式的支持重进入。
2、可以通过isHeldByCurrentThread
判断当前线程是否拿到锁。
3、构造器接收fairness参数,fairness=true表示公平锁反之是非公平锁。
4、tryLock() 无参方法没有遵循公平性,是非公平的。
5、公平锁的吞吐量没有非公平锁高,需要不断的进行线程之间的切换
ReentrantLock类结构
public class ReentrantLock implements Lock, java.io.Serializable
Lock所定义的方法。
- lock():获取锁,调用该方法的线程会获取锁。
- unlock(): 释放锁
- tryLock(): 尝试非阻塞的获取锁,获取成功返回true,失败返回false。
- tryLock(long,TimeUtil): 超时获取锁,有三种情况:当前线程在超时时间内获得了锁,当前线程在超时时间内被中断,超时时间结束,返回false。
- lockInterruptedException(): 可中断的获取锁,所在获取过程中可以中断当前线程。
公平锁与非公平锁
简言之,如果在绝对的时间上,先对锁发起请求的那个线程会先获取锁,遵循FIFO原则,也就是等待时间最长的那个线程最先获取锁。
公平锁和非公平锁只是相对于获取锁的时候而言。
如果一个锁是公平的,那么锁获取的顺序符合时间顺序。当一个线程获取了同步状态就可以认为获取到了锁,刚释放锁的线程下次再获取锁的概率非常大,使其他线程在同步队列中等待,因此非公平锁的线程切换次数很少,这是相对公平锁而言,所以吞吐量上,非公平锁更有优势,所以默认情况下,ReentrantLock的构造就是非公平锁。
互斥锁
互斥锁:同一时刻下,只有一个线程可以拿到锁,其他线程只能在同步队列中等待,只有拿到锁的线程释放锁,其他线程才能去争抢;
构造方法
public ReentrantLock() {
// 默认是非公平锁
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
从构造器中可以看出,公平锁是依靠 FairSync 实现的,非公平锁是依靠 NonfairSync 实现的。
Sync同步器
Sync继承了AQS,lock方法留给FairSync和NonFairSync两个子类实现,他实现了AQS的tryRelease方法和isHeldExclusively方法。
abstract static class Sync extends AbstractQueuedSynchronizer{
// 尝试释放锁
protected final boolean tryRelease(int releases) {
// releases一般是1,当前线程的同步状态-同步值
int c = getState() - releases;
// 当前线程没有持有锁,抛异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 当前线程持有的锁都释放了
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// c!=0代表是重入锁,锁没有释放完毕,前面已经用state-releases
setState(c);
return free; // return false,尝试释放锁失败
}
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();
}
}
加锁
先来看FairSync公平锁的lock方法和tryAcquire
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
// 尝试加锁,acquire方法是AQS的获取锁的方法,如果尝试获取锁失败,会进入同步队列中等待
final void lock() {
acquire(1);
}
/**
AQS的acquire
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
**/
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// hasQueuedPredecessors 是实现公平的关键
// 会判断当前线程是不是属于同步队列的头节点的下一个节点(头节点是释放锁的节点)
// 如果是(返回false),符合先进先出的原则,可以获得锁
// 如果不是(返回true),则继续等待
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果获取锁的线程再次请求锁,将通天阁不状态值增加并返回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;
}
}
再来看NonFairSync非公平锁的加锁和尝试获取锁
static final 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() {
// 通过判断当前线程是否获取锁的线程来决定获取操作是否成功,如果同步状态值成功修改,那就代表当前线程获取到了锁
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// 调用的是父类Sync的尝试获取锁的方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
释放锁
unlock 释放锁的方法,底层调用的是 Sync 同步器的 release 方法,release 是 AQS 的方法,分成两步:
- 尝试释放锁,如果释放失败,直接返回 false;
- 释放成功,从同步队列的头节点的下一个节点开始唤醒,让其去竞争锁。
第一步就是我们上文中 Sync 的 tryRelease 方法(4.1),第二步 AQS 已经实现了。
释放锁通过LockSupport.unpark(s.thread);
unLock 的源码如下:
// 释放锁
public void unlock() {
sync.release(1);
}
ReentrantLock-源码解析的更多相关文章
- 第六章 ReentrantLock源码解析2--释放锁unlock()
最常用的方式: int a = 12; //注意:通常情况下,这个会设置成一个类变量,比如说Segement中的段锁与copyOnWriteArrayList中的全局锁 final Reentrant ...
- 死磕 java同步系列之ReentrantLock源码解析(二)——条件锁
问题 (1)条件锁是什么? (2)条件锁适用于什么场景? (3)条件锁的await()是在其它线程signal()的时候唤醒的吗? 简介 条件锁,是指在获取锁之后发现当前业务场景自己无法处理,而需要等 ...
- ReentrantLock源码解析
ReentrantLock 1 数据结构 从上图可以看出,ReentrantLock的功能都是通过sync这个对象提供的. public class ReentrantLock implements ...
- 深入浅出ReentrantLock源码解析
ReentrantLock不但是可重入锁,而且还是公平或非公平锁,在工作中会经常使用到,将自己对这两种锁的理解记录下来,希望对大家有帮助. 前提条件 在理解ReentrantLock时需要具备一些基本 ...
- Java并发之ReentrantLock源码解析(二)
在了解如何加锁时候,我们再来了解如何解锁.可重入互斥锁ReentrantLock的解锁方法unlock()并不区分是公平锁还是非公平锁,Sync类并没有实现release(int arg)方法,这里会 ...
- Java并发之ReentrantLock源码解析(四)
Condition 在上一章中,我们大概了解了Condition的使用,下面我们来看看Condition再juc的实现.juc下Condition本质上是一个接口,它只定义了这个接口的使用方式,具体的 ...
- 死磕 java同步系列之ReentrantLock源码解析(一)——公平锁、非公平锁
问题 (1)重入锁是什么? (2)ReentrantLock如何实现重入锁? (3)ReentrantLock为什么默认是非公平模式? (4)ReentrantLock除了可重入还有哪些特性? 简介 ...
- ReentrantLock源码解析——虽众但写
在看这篇文章时,笔者默认你已经看过AQS或者已经初步的了解AQS的内部过程. 先简单介绍一下ReentantLock,跟synchronized相同,是可重入的重量级锁.但是其用法则相当不同,首先 ...
- Java并发之ReentrantLock源码解析(三)
ReentrantLock和BlockingQueue 首先,看到这个标题,不要怀疑自己进错文章,也不要怀疑笔者写错,哈哈.本章笔者会从BlockingQueue(阻塞队列)的角度,看看juc包下的阻 ...
- 第五章 ReentrantLock源码解析1--获得非公平锁与公平锁lock()
最常用的方式: int a = 12; //注意:通常情况下,这个会设置成一个类变量,比如说Segement中的段锁与copyOnWriteArrayList中的全局锁 final Reentrant ...
随机推荐
- spark中map和mapPartitions算子的区别
区别: 1.map是对rdd中每一个元素进行操作 2.mapPartitions是对rdd中每个partition的迭代器进行操作 mapPartitions优点: 1.若是普通map,比如一个par ...
- Python中open函数怎么操作文件
在 Python 中,如果想要操作文件,首先需要创建或者打开指定的文件,并创建一个文件对象,而这些工作可以通过内置的 open() 函数实现. open() 函数用于创建或打开指定文件,该函数的常用语 ...
- latex参考文献删除[.s.l],[.s.n]
用latex写毕业论文的插入会议参考论文的时候可能会遇到编译后的文档里面一堆[.s.l],[.s.n]的问题. 这是因为ref里面会议条目找不到地址信息,所以用[.s.l],[.s.n]替代,只需要更 ...
- 微信小程序自动化,记录趟过的坑!
项目思想:关键字+数据驱动混合测试 基于Android-微信小程序自动化的关键是:webview的切换 对于微信App来说如何从NATIVE切换到webview的过程 测试版本信息 1.微信版本:7. ...
- 最全总结 | 聊聊 Python 办公自动化之 PDF(上)
1. 前言 自动化办公,非 Python 莫属! 从本篇文章开始,我们继续聊聊自动化办公中另外一个常用系列:PPT 2. 准备一下 Python 操作 PPT 最强大的依赖库是:python-pptx ...
- 个人微信公众号搭建Python实现 -开发配置和微信服务器转入-配置说明(14.1.2)
@ 目录 1.查看基本配置 2.修改服务器配置 3.当上面都配置好,点击提交 4.配置如下 1.查看基本配置 登录到微信公众号控制面板后点击基本配置 这里要讲的就是订阅号 前往注册微信公众号 2.修改 ...
- Centos7.6上部署Supervisor来监控和操作各类服务
supervisor 是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系统.它可以很方便的监听.启动.停止.重启一个 ...
- Autofac的基本使用---目录
目录 Autofac的基本使用---1.前言 Autofac的基本使用---2.普通类型 Autofac的基本使用---3.泛型类型 Autofac的基本使用---4.使用Config配置 Autof ...
- Log4Net日志的简单使用示例
前言 源码参考示例地址 http://www.51aspx.com/Code/log4netusedemo/2707 本例博客园源码 https://files.cnblogs.com/files/m ...
- Spring中的注解@Value("#{}")与@Value("${}")的区别
1 @Value("#{}") SpEL表达式 @Value("#{}") 表示SpEl表达式通常用来获取bean的属性,或者调用bean的某个方法.当然还有可 ...