多线程高并发编程(3) -- ReentrantLock源码分析AQS
背景:
AbstractQueuedSynchronizer(AQS)
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable
介绍
- 提供一个框架,用于实现依赖先进先出(FIFO)等待队列的阻塞锁和相关同步器(信号量,事件等)。 该类被设计为大多数类型的同步器的有用依据,这些同步器依赖于单个原子
int值来表示状态。 子类必须定义改变此状态的受保护方法,以及根据该对象被获取或释放来定义该状态的含义。 给定这些,这个类中的其他方法执行所有排队和阻塞机制。 子类可以保持其他状态字段,但只以原子方式更新int使用方法操纵值getState(),setState(int)和compareAndSetState(int, int)被跟踪相对于同步。 - 子类应定义为非公共内部助手类,用于实现其封闭类的同步属性。 
AbstractQueuedSynchronizer类不实现任何同步接口。 相反,它定义了一些方法,如acquireInterruptibly(int),可以通过具体的锁和相关同步器来调用适当履行其公共方法。 - 此类支持默认独占模式和共享模式。【使用模板模式来定义是独占还是共享模式】 当以独占模式获取时,尝试通过其他线程获取不能成功。 多线程获取的共享模式可能(但不需要)成功。 除了在机械意义上,这个类不理解这些差异,当共享模式获取成功时,下一个等待线程(如果存在)也必须确定它是否也可以获取。 在不同模式下等待的线程共享相同的FIFO队列。 通常,实现子类只支持这些模式之一,但是两者都可以在
ReadWriteLock中发挥作用 。 仅支持独占或仅共享模式的子类不需要定义支持未使用模式的方法。 - 这个类定义的嵌套
AbstractQueuedSynchronizer.ConditionObject可用于作为一类Condition由子类支持独占模式用于该方法的实施isHeldExclusively()份报告是否同步排他相对于保持在当前线程,方法release(int)与当前调用getState()值完全释放此目的,和acquire(int),给定此保存的状态值,最终将此对象恢复到其先前获取的状态。AbstractQueuedSynchronizer方法将创建此类条件,因此如果不能满足此约束,请勿使用该约束。AbstractQueuedSynchronizer.ConditionObject的行为当然取决于其同步器实现的语义。 - 该类为内部队列提供检查,检测和监控方法,以及条件对象的类似方法。 这些可以根据需要导出到类中,使用
AbstractQueuedSynchronizer进行同步机制。 - 此类的序列化仅存储底层原子整数维持状态,因此反序列化对象具有空线程队列。 需要可序列化的典型子类将定义一个
readObject方法,可以将其恢复为readObject时的已知初始状态。 
用法
使用这个类用作同步的基础上,重新定义以下方法,【即在同步器中定义内部类重写下面的方法】,如适用,通过检查和/或修改使用所述同步状态
getState(),setState(int)或compareAndSetState(int,int)【通过这三个方法来保证原子性和线程安全性】:tryAcquire(int)独占模式获取锁tryRelease(int)独占模式释放锁tryAcquireShared(int)共享模式获取锁tryReleaseShared(int)共享模式释放锁isHeldExclusively()是否是独占式
每个这些方法默认抛出
UnsupportedOperationException。 这些方法的实现必须是线程安全的,通常应该是短的而不是阻止的。 定义这些方法是唯一支持使用此类的方法。 所有其他方法都被声明为final,因为它们不能独立变化。看下面的源码分析前可先观看多线程高并发编程(2) -- 可重入锁介绍和自定义
一.ReentrantLock的lock和unlock源码解析
lock流程:
- 调用同步器Sync的抽象方法lock,由公平锁FairSync实现
 - 调用AQS的acquire获取锁
- 尝试获取锁:公平锁FairSync的tryAcquire
- 第一次获取锁,若队列没有前节点和设置状态成功,存储当前线程,返回true,表示获取成功;
 - 如果是重入获取,锁持有数量+1;
 
 - 获取锁失败,把当前线程加入等待队列中,并对等待队列进行阻塞,不断竞争获取锁:acquireQueued遍历等待队列addWaiter,若有头节点则从队列中取出来,若没有则进行中断;
 
 - 尝试获取锁:公平锁FairSync的tryAcquire
 
unlock流程:
- 调用同步器的release方法,有AQS实现;
 - tryRelease释放锁,由Sync实现
- 锁数量-1;
 - 当前线程和保存的线程不一致抛出异常;
 - 锁数量为0则进行释放锁,把独占线程设置为null,修改状态;
 
 
//===========================ReentrantLock源码===============================
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;//同步器
public void lock() {//获取锁
sync.lock();//调用同步器Sync的lock,由FairSync实现
}
public void unlock() {//使用锁
sync.release(1);//调用同步器的release,由AQS实现
} //内部类,同步器继承AQS,实现tryRelease释放锁
abstract static class Sync extends AbstractQueuedSynchronizer{
abstract void lock();//获取锁抽象方法,由FairSync实现
//===========================释放锁===============================
protected final boolean tryRelease(int releases) {
int c = getState() - releases;//锁数量-1
//当前线程和保存的线程不一致
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//持有的锁数量为0
free = true;//释放锁
setExclusiveOwnerThread(null);//当前独占线程为null
}
setState(c);//设置状态
return free;
}
} //内部类,公平锁继承同步器,实现lock方法
static final class FairSync extends Sync {
//===========================获取锁===============================
final void lock() {
acquire(1);//调用AQS的acquire
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();//获得当前线程
/**getState是AQS的Node的waitStatus,其值有
*CANCELLED = 1
*SIGNAL = -1
*CONDITION = -2
*PROPAGATE = -3
*/
int c = getState();
//c初始值为0,0表示不是以上的状态;hasQueuedPredecessors之前是否有节点,
//如果是true表示这个线程的前面还有节点应该让前面的节点先获取锁,当前线程获取失败;
//【非公平锁少了hasQueuedPredecessors这个判断】
//compareAndSetState CAS比较,设置当前状态为1;setExclusiveOwnerThread当前线程设置为独占线程
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;//获取成功
}
}
else if (current == getExclusiveOwnerThread()) {//如果是当前线程,表示重入
int nextc = c + acquires;//锁数量+1
if (nextc < 0)//小于0表示溢出
throw new Error("Maximum lock count exceeded");
setState(nextc);//更新状态
return true;//获取成功
}
return false;//获取失败
}
}
} //=============AbstractQueuedSynchronizer源码==============
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable{
//===========================获取锁===============================
//以独占模式获取,忽略中断。通过调用至少一次tryAcquire(int)实现,成功返回。否则线程排队,
//可能会重复阻塞和解除阻塞,直到成功才调用tryAcquire(int)。
public final void acquire(int arg) {//FairSync的lock调用
//tryAcquire获取锁;acquireQueued线程加入到了等待队列中,进行阻塞等待,竞争获取锁;
//addWaiter其他线程获取锁失败添加到等待队列中;Node.EXCLUSIVE节点独占,为null
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected boolean tryAcquire(int arg) {//acquire调用,由FairSync实现
throw new UnsupportedOperationException();
}
final boolean acquireQueued(final Node node, int arg) {//acquire调用
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();//获取前一个节点
if (p == head && tryAcquire(arg)) {//如果获取的节点为头节点并且获取到锁
setHead(node);//当前节点设置为头节点
p.next = null;//头节点下一节点为空,即把当前节点从队列中移除出来
failed = false;
return interrupted;
}
//当前节点不是头节点,parkAndCheckInterrupt让当前线程处于阻塞等待状态由其他线程唤醒
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {//acquireQueued调用
int ws = pred.waitStatus;//获取前一节点的等待状态
if (ws == Node.SIGNAL)//如果状态为唤醒状态
return true;
if (ws > 0) {//处于CANCELLED状态
do {
node.prev = pred = pred.prev;//1.把所有处于CANCELLED状态的节点移除
} while (pred.waitStatus > 0);
pred.next = node;//2.把所有处于CANCELLED状态的节点移除
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);//设置为SIGNAL状态
}
return false;
}
private Node addWaiter(Node mode) {//acquireQueued的参数,在acquire中调用
Node node = new Node(Thread.currentThread(), mode);//创建Node,当前线程指向node
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) {//addWaiter调用
for (;;) {
Node t = tail;
if (t == null) { // 尾节点为空
if (compareAndSetHead(new Node()))//创建新节点并维护一个头节点
tail = head;//把当前节点设置为头节点
} else {
node.prev = t;//当前节点指向尾节点
if (compareAndSetTail(t, node)) {//把当前节点更新为尾节点
t.next = node;//前尾节点的下一节点指向当前尾节点
return t;
}
}
}
}
static void selfInterrupt() {//acquire调用
Thread.currentThread().interrupt();//当前线程中断
}
//===========================释放锁===============================
public final boolean release(int arg) {//ReentrantLock的unlock调用
if (tryRelease(arg)) {//当前线程锁释放成功,唤醒其他线程进行资源的竞争
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected boolean tryRelease(int arg) {//release调用,由Sync实现
throw new UnsupportedOperationException();
}
}
备注:公平锁是针对锁的获取而言,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序;非公平锁会进行插队获取锁;
二.AQS重写锁
流程:
- 实现Lock,重写实现方法lock、lockInterruptibly、tryLock、unlock、newCondition;
 - 内部类继承AQS,重写tryAcquire和tryRelease;
 
 public class MyAQSLock implements Lock{
     private MyAQS myAQS;
     private class MyAQS extends AbstractQueuedSynchronizer{
         @Override
         protected boolean tryAcquire(int arg) {
             int state = getState();//获取状态
             Thread thread = Thread.currentThread();
             if(state==0){//线程第一次进来获取,状态为0,表示可以拿到锁
                 if(compareAndSetState(0,arg)){//更新状态
                     setExclusiveOwnerThread(Thread.currentThread());//设置为独占线程,其他线程进来进入等待
                     return true;//获取成功
                 }
             }else if(getExclusiveOwnerThread()== thread){//重入,存储线程等于当前线程
                 setState(state+1);//锁数量+1
                 return true;
             }
             return false;//获取失败
         }
         @Override
         protected boolean tryRelease(int arg) {
             //当前线程不是存储线程
             if(Thread.currentThread() != getExclusiveOwnerThread()){
                 throw new RuntimeException();
             }
             int state = getState()-arg;//锁数量-1
             boolean flag = false;
             if(state==0){//锁数量为0
                 setExclusiveOwnerThread(null);//独占锁为null,表示可以让其他线程进来竞争获取资源了
                 flag=true;
             }
             setState(state);//更新状态
             return flag;
         }
         public ConditionObject newConditonObject(){
             return new ConditionObject();
         }
     }
     @Override
     public void lock() {
         myAQS.acquire(1);
     }
     @Override
     public void lockInterruptibly() throws InterruptedException {
         myAQS.acquireInterruptibly(1);
     }
     @Override
     public boolean tryLock() {
         return myAQS.tryAcquire(1);
     }
     @Override
     public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
         return myAQS.tryAcquireNanos(1,unit.toNanos(time));
     }
     @Override
     public void unlock() {
             myAQS.release(1);
     }
     @Override
     public Condition newCondition() {
         return myAQS.newConditonObject();
     }
 }
多线程高并发编程(3) -- ReentrantLock源码分析AQS的更多相关文章
- 多线程高并发编程(10) -- ConcurrentHashMap源码分析
		
一.背景 前文讲了HashMap的源码分析,从中可以看到下面的问题: HashMap的put/remove方法不是线程安全的,如果在多线程并发环境下,使用synchronized进行加锁,会导致效率低 ...
 - 多线程高并发编程(7) -- Future源码分析
		
一.概念 A Future计算的结果. 提供方法来检查计算是否完成,等待其完成,并检索计算结果. 结果只能在计算完成后使用方法get进行检索,如有必要,阻塞,直到准备就绪. 取消由cancel方法执行 ...
 - Java并发系列[5]----ReentrantLock源码分析
		
在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可 ...
 - Java并发编程之ReentrantLock源码分析
		
ReentrantLock介绍 从JDK1.5之前,我们都是使用synchronized关键字来对代码块加锁,在JDK1.5引入了ReentrantLock锁.synchronized关键字性能比Re ...
 - 多线程与高并发(三)—— 源码解析 AQS 原理
		
一.前言 AQS 是一个同步框架,关于同步在操作系统(一)-- 进程同步 中对进程同步做了些概念性的介绍,我们了解到进程(线程同理,本文基于 JVM 讲解,故下文只称线程)同步的工具有很多:Mutex ...
 - ReentrantLock 源码分析以及 AQS (一)
		
前言 JDK1.5 之后发布了JUC(java.util.concurrent),用于解决多线程并发问题.AQS 是一个特别重要的同步框架,很多同步类都借助于 AQS 实现了对线程同步状态的管理. A ...
 - Java并发编程-ReentrantLock源码分析
		
一.前言 在分析了 AbstractQueuedSynchronier 源码后,接着分析ReentrantLock源码,其实在 AbstractQueuedSynchronizer 的分析中,已经提到 ...
 - Java 多线程高并发编程 笔记(一)
		
本篇文章主要是总结Java多线程/高并发编程的知识点,由浅入深,仅作自己的学习笔记,部分侵删. 一 . 基础知识点 1. 进程于线程的概念 2.线程创建的两种方式 注:public void run( ...
 - java多线程---ReentrantLock源码分析
		
ReentrantLock源码分析 基础知识复习 synchronized和lock的区别 synchronized是非公平锁,无法保证线程按照申请锁的顺序获得锁,而Lock锁提供了可选参数,可以配置 ...
 
随机推荐
- 文件上传transferTo一行代码的bug
			
本次的项目环境为 Running with Spring Boot v1.5.10.RELEASE, Spring v4.3.14.RELEASE, 服务器环境为CentOS7.0. transfer ...
 - Scala尾递归
			
递归函数应用 首先,我们来对比两个递归方法的求值步骤. 假设有方法gcd,用来计算两个数的最大公约数.下面是欧几里得算法的实现: def gcp(a: Int, b: Int): Int = if ( ...
 - 一步一步学习S-MSCKF(一)连续时间IMU误差状态运动模型
			
1 IMU真实状态运动模型 状态向量: \(x_{I}=\left[{{_{G}^{I}{q(t)}}^{T},{b_{g}(t)}^{T},{^{G}v_{I}(t)}^{T},{b_{a}(t)} ...
 - 入门Python实现七夕表白神器
			
from PIL import Image, ImageDraw, ImageFont font_size = 7 #This var can change the Word's blank size ...
 - 【简介】OpenOCD 由jtag操作到parport driver流程
			
1. 定义 jtag_command_type 在 OpenOCD 中,JTag 命令在枚举 jtag_command_type 中定义,定义如下: /** * The type of the @c ...
 - nmap加载nse脚本在内网渗透中的使用-下
			
smb-ls.nse 列举共享目录内的文件,配合smb-enum-share使用nmap -p 445 <ip> --script smb-ls --script-args 'share= ...
 - 2020kali浏览器汉化等配置
			
0.修改搜索引擎 1. 2. 3.点击左侧搜索,输入language因为我已经修改为中文所以没有查询到结果 4点击搜索更多语言(未汉化未英文)找到chinese后添加 5.要将chinese上移到第一 ...
 - [ASP.NET Core 3.1]浏览器嗅探解决部分浏览器丢失Cookie问题
			
今天的干货长驱直入,直奔主题 看了前文的同学们应该都知道,搜狗.360等浏览器在单点登录中反复重定向,最终失败报错. 原因在于,非Chrome80+浏览器不识别Cookie上的SameSite=non ...
 - 【深度学习】perceptron(感知机)
			
目录 1.感知机的描述 2.感知机解决简单逻辑电路,与门的问题. 2.多层感应机,解决异或门 个人学习笔记,有兴趣的朋友可参考. 1.感知机的描述 感知机(perceptron)由美国学者Frank ...
 - Java基础语法(8)-数组中的常见排序算法
			
title: Java基础语法(8)-数组中的常见排序算法 blog: CSDN data: Java学习路线及视频 1.基本概念 排序: 是计算机程序设计中的一项重要操作,其功能是指一个数据元素集合 ...