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学习——用户自定义models问题解决
一.问题在Django中使用自定义的model的时候会出现下面的错误 ERRORS: auth.User.groups: (fields.E304) Reverse accessor for 'Use ...
- 剑指offer 栈的压入和弹出
题目描述输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈 ...
- JAVA集合框架的特点及实现原理简介
1.集合框架总体架构 集合大致分为Set.List.Queue.Map四种体系,其中List,Set,Queue继承自Collection接口,Map为独立接口 Set的实现类有:HashSet,Li ...
- APS助众生药业突破运营管理瓶颈
众生药业一直致力于为了世界提供世界级的产品及服务,成立以来公司先后实施了ERP系统,CRM系统,WMS系统,OA系统,精益生产,朝着行业信息化水平领先的目标迈进. 但近年随着业务量的不断扩大,仅仅拥有 ...
- 类型转换 String——》Char OR Char ——》String
String转换为char 在Java中将String转换为char是非常简单的.1. 使用String.charAt(index)(返回值为char)可以得到String中某一指定位置的char.2 ...
- ASP.NET----内置对象----Resuest
Request对象可以获取包含用户的计算机.页面及浏览器的请求等相关信息. Request对象的属性: ①Form----------返回有关表单变量的集合 ②QueryString--------- ...
- VirtualBox打开VMware虚拟机
下载安装VirtualBox 打开VirtualBox,选择新建 设置如下: 之后就可以直接打开虚拟机了.
- 面向对象(四)--绑定方法与非绑定方法(classmethod、staticmethod装饰器)
一.绑定方法与非绑定方法 1.绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入): (1)绑定给对象的方法:在类内部定义的函数(没有被任何装饰器修饰的)默认就是绑定给对象用的. (2)绑定 ...
- windows 给mysql安装innodb引擎
1.启用InnoDB 打开my.ini文件,找到[skip-innodb],在前面加# 2.更改数据库默认引擎为InnoDB 打开my.ini文件,更改[default-storage-e ...
- Acesrc and Travel(2019年杭电多校第八场06+HDU6662+换根dp)
题目链接 传送门 题意 两个绝顶聪明的人在树上玩博弈,规则是轮流选择下一个要到达的点,每达到一个点时,先手和后手分别获得\(a_i,b_i\)(到达这个点时两个人都会获得)的权值,已经经过的点无法再次 ...