ReentrantLock源码简析
概念
ReentrantLock,可重入锁。在多线程中,可以通过加锁保证线程安全。
加锁和解锁
- 加锁:
public void lock() {
sync.lock();
}
- 解锁
public void unlock() {
sync.release(1);
}
内部类Sync继承AQS(AbstractQueuedSynchronizer),因此可以维护状态变量state,通过acquire()获取state、release()释放state。后文会涉及。
构造方法
- 无参构造方法
默认使用非公平锁。
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
- 有参构造方法
根据参数判断是否公平锁。
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
Sync类
Sync类是一个继承AQS的内部静态抽象类。
Sync继承AQS,因此也可以维护状态变量state,通过acquire()获取state、release()释放state。
公平锁FairSync和非公平锁NonfairSync,都继承Sync类,并且重写了Sync类中的抽象方法Lock()。
其中的nonfairTryAcquire()方法,是ReentrantLock最核心的实现。
如果状态变量state为0,说明目前还没有其他线程加锁,当前线程可以进行加锁,通过CAS将状态变量state加1。
并且当前线程会获得锁。
如果状态变量state不为0,且获取锁的是当前线程,则将state加1。
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static 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();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//如果状态变量state为0,说明目前还没有其他线程加锁,当前线程可以进行加锁,通过CAS将状态变量state加1。
//并且当前线程会获得锁。
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果状态变量state不为0,且获取锁的是当前线程,则将state加1。
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
//释放state
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果state为0,则说明已经释放锁,当前没有线程获得锁。
if (c == 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 the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
非公平锁和公平锁
非公平锁和公平锁,都重写了tryAcquire()。而acquire()是AQS中的方法。
- 非公平锁:
非公平锁的调用过程: lock()---> acquire(1) ---> tryAcquire(1) ---> nonfairTryAcquire(1)
/**
* Sync object for non-fair locks
*/
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);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
- 公平锁:
主要流程:lock()--->acquire(1)--->tryAcquire(1)---> hasQueuedPredecessors()
公平锁和非公平锁的主要区别是:
公平锁,会去判断“当前线程”是不是AQS的线程等待队列(CLH队列)中的第一个线程,并对state进行CAS操作。
这个区别在hasQueuedPredecessors()方法中体现。
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* 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();
//如果当前状态变量为0(也就是没有线程获取到锁)
if (c == 0) {
//判断“当前线程”是不是AQS的线程等待队列(CLH队列)中的第一个线程,并对state进行CAS操作。
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//设置当前线程获取锁
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;
}
}
ReentrantLock和Synchronized的区别
- Synchronized是基于监视锁monitor实现的,当monitor为0时,线程能够获得锁,此时monitor加1。当有线程进行请求时,判断monitor是否为0,如果不为0,说明锁已经被其他线程拿到了。Synchronized是JVM级别的锁。
而ReentrantLock是基于AQS实现的,AQS是一个抽象队列容器。当有线程发起请求时,如果是公平锁,需要在队列中排除等待。如果是非公平锁,会插队。
- Synchronized可以是方法锁、对象锁。
而ReentrantLock可以在代码中灵活地加锁、解锁。但要注意,必须在finally中进行unlock()。
其他资料
AQS详情见: https://www.cnblogs.com/expiator/p/12052125.html
ReentrantLock源码简析的更多相关文章
- SpringMVC学习(一)——概念、流程图、源码简析
学习资料:开涛的<跟我学SpringMVC.pdf> 众所周知,springMVC是比较常用的web框架,通常整合spring使用.这里抛开spring,单纯的对springMVC做一下总 ...
- Flink源码阅读(一)——Flink on Yarn的Per-job模式源码简析
一.前言 个人感觉学习Flink其实最不应该错过的博文是Flink社区的博文系列,里面的文章是不会让人失望的.强烈安利:https://ververica.cn/developers-resource ...
- django-jwt token校验源码简析
一. jwt token校验源码简析 1.1 前言 之前使用jwt签发了token,里面的头部包含了加密的方式.是否有签名等,而载荷中包含用户名.用户主键.过期时间等信息,最后的签名还使用了摘要算法进 ...
- 0002 - Spring MVC 拦截器源码简析:拦截器加载与执行
1.概述 Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理.例如通过拦截器可以进行权限验证.记录请求信息的日 ...
- OpenStack之Glance源码简析
Glance简介 OpenStack镜像服务器是一套虚拟机镜像发现.注册.检索. glance架构图: Glance源码结构: glance/api:主要负责接收响应镜像管理命令的Restful请求, ...
- AFNetworking源码简析
AFNetworking基本是苹果开发中网络请求库的标配,它是一个轻量级的网络库,专门针对iOS和OS X的网络应用设计,具有模块化的架构和丰富的APIs接口,功能强大并且使用简单,深受苹果应用开发人 ...
- ElementUI 源码简析——源码结构篇
ElementUI 作为当前运用的最广的 Vue PC 端组件库,很多 Vue 组件库的架构都是参照 ElementUI 做的.作为一个有梦想的前端(咸鱼),当然需要好好学习一番这套比较成熟的架构. ...
- DRF之APIView源码简析
一. 安装djangorestframework 安装的方式有以下三种,注意,模块就叫djangorestframework. 方式一:pip3 install djangorestframework ...
- spring ioc源码简析
ClassPathXmlApplicationContext 首先我们先从平时启动spring常用的ClassPathXmlApplicationContext开始解析 ApplicationCont ...
随机推荐
- Django的学习——全局的static和templates的使用
一.问题 首先我们在进行Django框架搭建的时候我们需要建立一个全局的变量,一是为了实现代码的复用,二是为了方便管理,如下图的样式 二.解决 1.修改setting里面的配置文件①templates ...
- 开源分布式追踪系统 — Jaeger介绍
目录 一.Jaeger是什么 二.Jaeger架构 1. 术语 2. 架构图 三.关于采样率 四.部署与实践 一.Jaeger是什么 Uber开发的一个受Dapper和Zipkin启发的分布式跟踪系统 ...
- mysqlbinlog恢复误删数据
概述 代码bug,在处理上传出现异常时执行了DELETE FROM t_resource WHERE resource_id = ? OR parent_id = ?因为OR条件导致用户的上传的所有数 ...
- vs 发版时,在发版的文件夹中,找不到应该有的某个文件
检查:VS中,这个文件右击属性,查看生成操作.如果是“无”,改为“内容”.再重新发布就没问题了. 想看发版出来的内容包括哪些,可以从“发布”--“应用程序文件”查看
- SQL参数化出现执行慢问题
问题: 应用程序通过参数化访问数据库时出现执行效率慢,直接通过语句查时秒出结果,通过参数化查询时用时是前者的几十上百倍. 排查:1.使用了错误的执行计划; 解决:清除缓存的执行计划(未能解决上面问题) ...
- win10安装MySQL5.6.
我分了两种下载安装的方式给大家看,注意数据库这个东西不在乎版本是不是最新,在乎的是够稳定,现在公司中常用的是mysql5.5和mysql5.6的版本,我现在就用mysql5.6的版本给大家来演示一下: ...
- 软件开发的生产力vs质量
在<人月神话>里看到引用的一篇论文,<没有银弹:软件工程的本质性与附属性工作>(英语:No Silver Bullet-Essence and Accidents of Sof ...
- vue开发相关问题以及解决方案
1.使用ref获取到dom元素,但是无法获取到dom元素的样式? 给div盒子加上<div ref="remove">, 在控制台打印为undefined. 此时我们打 ...
- python列表推导式及其简单应用
列表推导式(又称列表解析式)提供了一种简明扼要的方法来创建列表 一个简单平方 普通for循环 for i in range(1,5): print(i*i,end='') 列表推导式 res = [x ...
- android studio学习---怎么创建一个新的module并且再次运行起来(在当前的project里面)
选择File->new module出现的界面,选择android application选择下一步,就出现了和刚刚一样的流程了,一步步创建完成即可. 我们看到多了个secondAndroid的 ...