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信 ...
随机推荐
- k8s安装部署成功
- Raft翻译
英文原文:https://web.stanford.edu/~ouster/cgi-bin/papers/raft-atc14 In Search of an Understandable Conse ...
- vue-cli项目上传到github预览问题
上传前先npm run build 后git push origin master 问题:chunk无法加载? 原因:在github.io请求chunk时,chunk的url使用的是publicPat ...
- Spring 中基于 AOP 的 @AspectJ
Spring 中基于 AOP 的 @AspectJ @AspectJ 作为通过 Java 5 注释注释的普通的 Java 类,它指的是声明 aspects 的一种风格. 通过在你的基于架构的 XML ...
- spring的各种注入
java package cn.ioc.student; import java.util.List; import java.util.Map; import java.util.Set; publ ...
- centos7 安装rpm版的mysql遇到坑——误删root用户的恢复
在网上找了教程http://blog.csdn.net/frankcheng5143/article/details/77609093安装过程很顺利,随着修改了root的密码后不下心误删了root账号 ...
- DQN(Deep Q-learning)入门教程(三)之蒙特卡罗法算法与Q-learning算法
蒙特卡罗法 在介绍Q-learing算法之前,我们还是对蒙特卡罗法(MC)进行一些介绍.MC方法是一种无模型(model-free)的强化学习方法,目标是得到最优的行为价值函数\(q_*\).在前面一 ...
- Jenkins在Pod中实现Docker in Docker并用kubectl进行部署
Jenkins在Pod中实现Docker in Docker并用kubectl进行部署 准备工作 安装Jenkins Jenkins的kubernetes-plugin使用方法 说明 Jenkins的 ...
- Misdirection: 1靶机writeup
看下端口 nmap -A 172.16.61.131 一些坑3306无法访问,80,web2py漏洞无法利用 利用dirb遍历网站路径 得到下面命令执行漏洞 http://172.16.61.131: ...
- 【Java8新特性】关于Java8中的日期时间API,你需要掌握这些!!
写在前面 Java8之前的日期和时间API,存在一些问题,比如:线程安全的问题,跨年的问题等等.这些问题都在Hava8中的日期和时间API中得到了解决,而且Java8中的日期和时间API更加强大.立志 ...