Java 线程 — AbstractQueuedSynchronizer
锁
锁就是一种状态,比如互斥锁:同一时间只能有一个线程拥有,可以使用一个整型值来标志当前的状态
- 0:表示没有现成占有锁
- 1:表示锁已经被占用
AbstractQueuedSynchronizer
- 实现Java中锁的基础,提供了一系列模板方法,程序员可以继承该类作为内部类实现自定义同步语义。
- 通过一个整型变量state维护锁的同步状态
- 通过CAS保证同步状态state的修改是原子的
这个抽象类中使用了很多模板方法,在实现锁机制的时候可以调用这些模板方法:

模板方法里面调用了一些尚未实现、留给程序员实现的抽象方法:
独占式同步方法:

共享式同步方法:

独占式同步状态
同时只能有一个线程占有锁,独占式同步过程:
// 获取锁,可以在在lock方法中调用
public final void acquire(int arg) {
// tryAcquire方法需要根据锁的功能自行实现
// 获取锁失败的话执行addWaiter,Node.EXCLUSIVE表明当前node获取的是独占式的锁
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 因为获取锁失败,将当前node加入queue的最后一个节点tail
private Node addWaiter(Node mode) {
// 新建的node是独占模式,不对waitStatus赋值,也就是默认为0
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
// 如果队列不为空,则插入tail
if (pred != null) {
node.prev = pred;
// 因为是独占式的锁,只会有一个线程修改tail,不要循环
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 如果tail == null表示队列为空,调用enq入队
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
// 因为在这个时候可能其他线程已经入队,队列可能不再为null
// 如果依然为空,则初始化队列——新建一个node,并设为head=tail=newNode
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 如果其他线程已经入过队,则正常设置tail
node.prev = t;
if (compareAndSetTail(t, node)) {
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(); // 获取前一个node
// 如果前一个node是head,则尝试获取锁,因为如果前一个是head说明head有可能已经释放锁,当前node可以尝试获取锁
if (p == head && tryAcquire(arg)) {
// 如果获取成功则将当前node设为head
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 判断是否需要park
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
// 这个方法可以结合释放锁release(释放独占锁)和releaseShared(释放共享锁)来看,因为释放锁的时候会判断是否进行unpark操作
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.
*/
// 前面一个节点是signal,表示前面一个节点释放锁的时候(release)一定会unpark,那么当前node可以park(因为一定会被unpark,保证不会被永久阻塞)
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
// 把已经是cancelled的node从队列中移除,不再进行获取锁
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;
}
释放独占锁:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
// 独占锁的node.waitStatus默认是0,因为在初始化的时候不会对waitStatus赋值,如果不等于0,表示在加入队列的时候设置过waitStatus,设置为SIGNAL
if (h != null && h.waitStatus != 0)
// 如果head的waitStatus是SIGNAL表示要唤醒阻塞的线程
unparkSuccessor(h);
return true;
}
return false;
}
共享式同步状态
可以同时有多个线程获得锁
重入锁
同一个线程重复获得锁
读写锁
读锁:共享锁
写锁:独占锁
LockSupport
调用Unsafe的方法,负责阻塞或者唤醒线程。相当于一个线程有一个许可,默认是被占用的,park会占用这个许可,unpark会分配这个许可,只要调用过至少一次unpark,那么再调用一次park线程会返回,不会被永久阻塞:
- 如果先调用park并且后面不会调用unpark,那么线程会被一直阻塞;
- 如果先调用unpark(至少一次),再调用park,那么线程立即返回,不会阻塞
- 如果先调用park(至少一次),再调用unpark,那么线程在调用unpark后返回,不会继续阻塞
所以unpark会给当前线程分配一个许可,如果之前调用过一次park或者后面调用一次park,那么线程不再继续阻塞,即:
- 一个线程只有一个许可
- unpark分配许可
- park占用许可
- 只有调用unpark的次数 >= 调用park的次数,线程才不会被永久阻塞
Condition
任意一个Java对象,都拥有一组Monitor方法(定义在Object上),包括:wait,notify等,与synchronized配合实现等待/通知模式
Lock和Condition结合也可以实现等待/通知模式
Java 线程 — AbstractQueuedSynchronizer的更多相关文章
- Java 线程池框架核心代码分析--转
原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...
- Java线程问题分析定位
Java线程问题分析定位 分析步骤: 1.使用top命令查看系统资源占用情况,发现Java进程占用大量CPU资源,PID为11572: 2.显示进程详细列表命令:ps -mp 11572 -o THR ...
- 怎样分析java线程堆栈日志
注: 该文章的原文是由 Tae Jin Gu 编写,原文地址为 How to Analyze Java Thread Dumps 当有障碍,或者是一个基于 JAVA 的 WEB 应用运行的比预期慢的时 ...
- Java 线程池框架核心代码分析
前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executor接口,提供了一种标准的方法将任务的提交过 ...
- Java线程同步之一--AQS
Java线程同步之一--AQS 线程同步是指两个并发执行的线程在同一时间不同时执行某一部分的程序.同步问题在生活中也很常见,就比如在麦当劳点餐,假设只有一个服务员能够提供点餐服务.每个服务员在同一时刻 ...
- Java线程池使用和分析(二) - execute()原理
相关文章目录: Java线程池使用和分析(一) Java线程池使用和分析(二) - execute()原理 execute()是 java.util.concurrent.Executor接口中唯一的 ...
- Java 线程同步组件 CountDownLatch 与 CyclicBarrier 原理分析
1.简介 在分析完AbstractQueuedSynchronizer(以下简称 AQS)和ReentrantLock的原理后,本文将分析 java.util.concurrent 包下的两个线程同步 ...
- 并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)
史上最清晰的线程池源码分析 鼎鼎大名的线程池.不需要多说!!!!! 这篇博客深入分析 Java 中线程池的实现. 总览 下图是 java 线程池几个相关类的继承结构: 先简单说说这个继承结构,E ...
- Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理
相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...
随机推荐
- jboss developers studio 快速创建 spring mvc 项目
1. 2. 部署运行 还有一个 rest very good !! ps:其实就是 一个 jboss 的 spring mvc maven 原型
- iOS开发-二维码的基本使用
二维码在生活中出现的频率越来越多了,大街小巷各个角落,它的出现更大的作用是代替功能单一的传统条形码,但是通常很多人第一次见到这个都不清楚这是干嘛用的.最初第一次见到可能就是买到的商品上有一个数字的条形 ...
- js实现xml转化成字符串
xml字符串转xml dom经常遇到在js里面需要解析xml的问题,然而有时候,后台返回的不是dom 而是string 字符串,需要将字符串转换成dom对象,然后才可以进行节点值解析和读取functi ...
- 使用SQL语句逐条更新每条记录
有些时候,我们希望同时更新表中的多条记录,但更新的值不一样. declare @i int declare @j int declare @phone varchar(20) set @j=1 sel ...
- 无鼠标Windows操作
1.常用tab键,方便跳转 2.打开软件方式: 1.win+1,2,3...依序打开任务栏图标.常用软件可以放在这里: 2.创建quickStart文件夹,配置路径,将所有要用到的功能都放在这里.( ...
- Qt实现的根据进程名来结束进程
1.头文件及实现部分: #include <windows.h> #include <tlhelp32.h> #include "psapi.h" #pra ...
- http://blog.sina.com.cn/s/blog_4c3b6a070100etad.html
http://blog.sina.com.cn/s/blog_4c3b6a070100etad.html
- mysql sql_safe_updates 不支持子查询的更新。
考虑到开发人员有时候不小心误更新数据,要求线上库的 MySQL 实例都设置 sql_safe_updates=1 来避免没有索引的 update.delete. 结果有一天开发发现下面的一个SQL 没 ...
- 深入理解openstack网络架构(1)
原文地址: https://blogs.oracle.com/ronen/entry/diving_into_openstack_network_architecture 译文转载自:http://b ...
- 解决.NET Core中MailKit无法使用阿里云邮件推送服务的问题
在博问中(.net core怎么实现邮件发送)知道了MailKit无法使用阿里云邮件推送服务发送邮件的问题,自已实测也遇到同样的问题,而用自己搭建的邮件服务器没这个问题. 于是,向阿里云提交了工单.. ...