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信 ...
随机推荐
- 数学分析新讲(1) NOTE
前言:无聊才翻翻看看来复习啦..所以慢更(●'◡'●) 1.利用求和公式的性质推导: \[\sum^{n}_{k=1}k=n \] \[\sum^{n}_{k=1}k^2=\frac{n(n+1)(2 ...
- 关于python中第三方库安装方法和问题解决
一.安装方法 方法一: 1.管理员身份启动命令行(运行--->cmd) 2.pip install 库的绝对路径和库的详细名称 :或者运用cd命令跳转到下载好的库所在的位置然后pip insta ...
- Mysql之Linux中mariadb主从复制
master主机mysql安装配置 1.下载mariadb(Centos7开始mysql的名字) (1)其它方式(不推荐):rpm安装/软件源安装 (2)yum安装(推荐): ①centos官方的yu ...
- Django之JSON数据格式
JSON简介: o JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation) o JSON 是轻量级的文本数据交换格式 o JSON ...
- linux ,mac连接, git pull error, chmod修改文件的权限/chown修改文件和目录的所有者
去项目目录下 启动服务 setsid npm start & Mac下如何用SSH连接远程Linux服务器 https://www.cnblogs.com/littleBit/p/536280 ...
- poj2391 最大流+拆点+二分答案+Floyd
Ombrophobic Bovines Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 19553 Accepted: 4 ...
- git rebase 还是 merge的使用场景最通俗的解释
什么是 rebase? git rebase 你其实可以把它理解成是“重新设置基线”,将你的当前分支重新设置开始点.这个时候才能知道你当前分支于你需要比较的分支之间的差异. 原理很简单:rebase需 ...
- thymeleaf将对象ModelList数据抛到HTML页面
- Java基础语法--分支结构
if-else 结构 if(条件表达式){ 执行代码块; } if(条件表达式){ 执行代码块; }else { 执行代码块; } if(条件表达式){ 执行代码块; }else if (条件表达式) ...
- 3.key的操作
我们之前使用Redis简单存储了三个参数: 在语句set name jack中,其中name就是一个key.我们Java中的变量名是有一定规则的,比如组成内容可以是“数字”,“字母”以及“下划线”. ...