ReentrantLock原理分析
一 UML类图

通过类图ReentrantLock是同步锁,同一时间只能有一个线程获取到锁,其他获取该锁的线程会被阻塞而被放入AQS阻塞队列中。ReentrantLock类继承Lock接口;内部抽象类Sync实现抽象队列同步器AbstractQueuedSynchronizer;Sync类有两个子类NonfairSync(非公平锁)和FairSync(公平锁),默认构造方法使用非公平锁,可以使用带布尔参数的构造方法指定使用公平锁;ReentrantLock可以创建多个条件进行绑定。
二 原理分析
2.1 获取锁
2.1.1 void lock()方法
调用线程T调用该方法尝试获取当前锁。
①如果锁未被其他线程获取,则调用线程T成功获取到当前锁,然后设置当前锁的拥有者为调用线程T,并设置AQS的状态值state为1,然后直接返回。
②如果调用线程T之前已经获取当前锁,则只设置设置AQS的状态值state加1,然后返回。
③如果当前锁已被其他线程获取,则调用线程T放入AQS队列后阻塞挂起。
public void lock() {
sync.lock();//委托内部公平锁和非公平锁获取锁
}
//非公平锁
final void lock() {
if (compareAndSetState(0, 1))//设置AQS状态值为1
setExclusiveOwnerThread(Thread.currentThread());//成功设置锁的线程拥有者
else
acquire(1);//失败加入到AQS队列阻塞挂起
}
//公平锁
final void lock() {
acquire(1);
}
非公平锁分析:
//调用父类AbstractOwnableSynchronizer方法CAS设置state值,成功返回true,失败返回false
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
//调用父类AbstractOwnableSynchronizer方法,设置当前线程为锁的拥有者
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
//调用AbstractQueuedSynchronizer父类方法,第一次获取锁失败
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//排它锁类型
selfInterrupt();
}
//NonfairSync子类,重写父类方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
//Sync类非公平锁尝试获取锁
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {//二次获取锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//当前线程已获取锁,AQS状态值加1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
//AbstractQueuedSynchronizer类创建节点,添加到AQS队列后面
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);//创建AQS队列的节点,节点类型排它锁
Node pred = tail;//尾结点
if (pred != null) {
node.prev = pred;//新节点的前一个节点是尾结点
if (compareAndSetTail(pred, node)) {//CAS机制添加节点
pred.next = node;//尾结点执行新的节点
return node;
}
}
enq(node);
return node;
}
//插入节点到队列中
private Node enq(final Node node) {
for (;;) {
Node t = tail;//尾结点
if (t == null) { // 尾结点为空,初始化
if (compareAndSetHead(new Node()))//第一步:CAS创建头结点(哨兵节点)一个空节点
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {//第二步:CAS设置尾结点
t.next = node;
return t;
}
}
}
}
//
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();//新节点的前节点
if (p == head && tryAcquire(arg)) {//如果p节点的前节点是头结点,并且尝试给锁枷锁
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);//失败解锁
}
}
//阻塞挂起当前线程
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
//
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.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;
} 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);
}
return false;
}
//
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
公平锁分析:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
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;
}
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
//①h != t:表示AQS队列头结点和尾结点不相同,队列不为空;
//②(s = h.next) == null || s.thread != Thread.currentThread():头结点(哨兵节点)为空或者next节点不等于当前线程
return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
}
2.1.2 void lockInterruptibly()方法
与2.2.1方法相似,不同之处在于:该方法对中断进行响应,其他线程调用当前线程中断方法,抛出InterruptedException。
2.1.3 boolean tryLock()方法
尝试获取锁。注意:该方法不会引起当前线程阻塞。
2.1.4 boolean tryLock(long timeout, TimeUnit unit)方法
与2.1.3方法相似,不同之处在于:设置了超时时间。
2.2 释放锁
尝试释放锁。
如果当前线程T已获取锁,则调用该方法更新AQS状态值减1。如果当前状态值为0,则释放锁;如果当前状态值部位0,则只是减1操作。
如果当前线程T未获取锁,则调用该方法是会抛出IllegalMonitorStateException异常。
2.2.1 void unlock()方法
public void unlock() {
sync.release(1);
}
//调用AbstractQueuedSynchronizer方法
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//唤醒线程
return true;
}
return false;
}
//回调Sync类释放锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);//设置锁的拥有线程为空
}
setState(c);
return free;
}
//
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;//线程阻塞等待状态
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);//CAS设置状态
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)//遍历AQS队列
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);//唤醒线程
}
h != t
ReentrantLock原理分析的更多相关文章
- Java 重入锁 ReentrantLock 原理分析
1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...
- Java 线程同步组件 CountDownLatch 与 CyclicBarrier 原理分析
1.简介 在分析完AbstractQueuedSynchronizer(以下简称 AQS)和ReentrantLock的原理后,本文将分析 java.util.concurrent 包下的两个线程同步 ...
- java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析
java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...
- Java 线程池原理分析
1.简介 线程池可以简单看做是一组线程的集合,通过使用线程池,我们可以方便的复用线程,避免了频繁创建和销毁线程所带来的开销.在应用上,线程池可应用在后端相关服务中.比如 Web 服务器,数据库服务器等 ...
- HashMap 与 ConcrrentHashMap 使用以及源码原理分析
前奏一:HashMap面试中常见问题汇总 HashMap的工作原理是近年来常见的Java面试题,几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和Has ...
- ConcurrentHashMap原理分析(1.7与1.8)-put和 get 需要执行两次Hash
ConcurrentHashMap 与HashMap和Hashtable 最大的不同在于:put和 get 两次Hash到达指定的HashEntry,第一次hash到达Segment,第二次到达Seg ...
- Android视图SurfaceView的实现原理分析(示例,出错代码)
在Android系统中,有一种特殊的视图,称为SurfaceView,它拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面.由于拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独 ...
- AQS工作原理分析
AQS工作原理分析 一.大致介绍1.前面章节讲解了一下CAS,简单讲就是cmpxchg+lock的原子操作:2.而在谈到并发操作里面,我们不得不谈到AQS,JDK的源码里面好多并发的类都是通过Sy ...
- Handler系列之原理分析
上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...
随机推荐
- ESXI 6.5利用Centos7重置root密码
ESXI6.5宿主机,很久没有登录,再次登录的时候,发现忘记root密码了 1.先将刻录一个CentOS7的启动光盘或U盘,并将服务器的启动项修改为光盘 2.保存BIOS重启后,选择Troublesh ...
- 浅谈产品模型(Profile)在程序设计中的作用
引言:物联网平台的一个重要功能就是资产管理,产品或者设备都可以看成是资产中组成部分,所以有时候说物联网平台可以进行产品管理和设备管理.通常应用物联网平台开发一套具有产品或者设备管理功能的系统的时候,必 ...
- 用matplotlib和pandas绘制股票MACD指标图,并验证化交易策略
我的新书<基于股票大数据分析的Python入门实战>于近日上架,在这篇博文向大家介绍我的新书:<基于股票大数据分析的Python入门实战>里,介绍了这本书的内容.这里将摘录出部 ...
- fastclick从接触到丢弃
fastclick简介 fastclick是一款为了解决移动端300ms点击延迟而诞生的插件. 在移动端,如果对页面没有做任何处理,点击一个元素,触发的事件流程可简单理解为:touch -> 经 ...
- springmvc-初次接触
一,mvc做的事情 1,将url映射到java类或者java的方法. 2,封装用户提交的数据 3,处理请求--调用相关的业务处理--封装想相应的数据 4,将相应数据进行渲染,jsp或者html 二,s ...
- 转 js自定义事件——Event和CustomEvent
之前在学习自定义事件时,在MDN的Event.initEvent()页面顶端有写:该特性已从Web标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性. 作 ...
- Python __str__(self)
python 在打印一个实例化对象时,打印的是对象的地址,比如:<__main__.Workers object at 0x00000000255A9AC8> 而__str__(self) ...
- mantis2.22.1中添加管理员密码修改框
1.修改文件 mantis/manage_user_edit_page.php 找到<!-- Email -->位置,将以下代码粘贴到下面即可:<tr <?php echo h ...
- Java岗位面试题分享:jvm+分布式+消息队列+协议(已拿offer)
个人近期面试情况 今年二月以来,我的面试除了一个用友的,基本其他都被毙了,可以说是非常残酷的.其中有很多自己觉得还面的不错的岗位,比如百度.跟谁学.好未来等公司.说实话,打击比较大. 情况基本上是从三 ...
- 应小姐姐要求,整理常用Git操作命令,她都学会了,你确定不收藏
前言 因为个人原因,转化了部门之后已经很久没有接触过开发层级的东西了,好多东西基本都忘记了,但是新的部门有时候会用到相应的研发部的代码和文档手册,所以耳边就充斥这一句话 这个为什么下载不了?这个为什么 ...