一 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原理分析的更多相关文章

  1. Java 重入锁 ReentrantLock 原理分析

    1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...

  2. Java 线程同步组件 CountDownLatch 与 CyclicBarrier 原理分析

    1.简介 在分析完AbstractQueuedSynchronizer(以下简称 AQS)和ReentrantLock的原理后,本文将分析 java.util.concurrent 包下的两个线程同步 ...

  3. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  4. Java 线程池原理分析

    1.简介 线程池可以简单看做是一组线程的集合,通过使用线程池,我们可以方便的复用线程,避免了频繁创建和销毁线程所带来的开销.在应用上,线程池可应用在后端相关服务中.比如 Web 服务器,数据库服务器等 ...

  5. HashMap 与 ConcrrentHashMap 使用以及源码原理分析

    前奏一:HashMap面试中常见问题汇总 HashMap的工作原理是近年来常见的Java面试题,几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和Has ...

  6. ConcurrentHashMap原理分析(1.7与1.8)-put和 get 需要执行两次Hash

    ConcurrentHashMap 与HashMap和Hashtable 最大的不同在于:put和 get 两次Hash到达指定的HashEntry,第一次hash到达Segment,第二次到达Seg ...

  7. Android视图SurfaceView的实现原理分析(示例,出错代码)

    在Android系统中,有一种特殊的视图,称为SurfaceView,它拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面.由于拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独 ...

  8. AQS工作原理分析

      AQS工作原理分析 一.大致介绍1.前面章节讲解了一下CAS,简单讲就是cmpxchg+lock的原子操作:2.而在谈到并发操作里面,我们不得不谈到AQS,JDK的源码里面好多并发的类都是通过Sy ...

  9. Handler系列之原理分析

    上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...

随机推荐

  1. 科技感满满,华为云DevCloud推出网页暗黑模式

    近期,华为云DevCloud推出了暗黑模式,让用户在网页端也可以体验到桌面级应用才有的特性.   深色模式(Dark Mode),俗称暗黑模式.是近2年以来用户呼声最高的功能之一,一些国外顶级厂商都将 ...

  2. Least Cost Bracket Sequence(贪心)

    Least Cost Bracket Sequence(贪心) Describe This is yet another problem on regular bracket sequences. A ...

  3. Java注解的定义和使用

    注解也叫元数据,一种代码级别的说明.是jdk1.5后产生的一个特性,与类.接口.枚举同一个档次,他可以在包.类.字段.方法.局部变量.方法参数等的前面,用来对这些元素进行说明.注释: <!--m ...

  4. RobotFramework自动化测试之元素定位

    前言:最近在做基于RF框架的Web自动化测试,其中涉及到元素的定位,主要用到id.name.xpath.css四中定位方法,尤其后面的两种方法特别有效,可以解决大部分的定位问题. id和name定位 ...

  5. gentoo 下安装lamp

    更新系统 emerge --sync emerge --update world 安装apache emerge www-servers/apache rc-update add apache2 de ...

  6. Tensorflow从0到1(一)之如何安装Tensorflow(Windows和Linux两种版本)

    现在越来越多的人工智能和机器学习以及深度学习,强化学习出现了,然后自己也对这个产生了点兴趣,特别的进行了一点点学习,就通过这篇文章来简单介绍一下,关于如何搭建Tensorflow以及如何进行使用.建议 ...

  7. lonic常用组件之五------按钮

    一.Ionic常用组件之五------按钮 <ion-button  color="主题色"   size="small/large"  expand=& ...

  8. python3.x 基础三:字符集问题

    总结了一张表,更详细信息百度百科: 序号 年份 编码 标准协会 特点 二进制长度 字符长度 表现 1 1967 ASCII 美国国家标准学会(American National Standard In ...

  9. Mac node.js express-generator脚手架安装

    前言 由于本人在学习NodeJs的express框架时,在Mac电脑上安装express遇到了一个深痛的坑点,特写此文来记录.该坑点的解决方案我在国内的度娘没有找到,问别人也没有方案,最后通过goog ...

  10. (二)vue的生命周期及相关的运行流程

    @ 目录 1. vue的生命周期 经过一系列的初始化过程 需要设置数据监听 编译模板 将实例花在到DOM 并在数据变化时更新DOM 等 这是相应的生命周期函数 在用的时候直接写上, 其实很多人都这样, ...