当前位置:网站首页>JUC锁框架——初识AQS
JUC锁框架——初识AQS
2022-08-04 05:32:00 【real沛林】
AQS:AbstractQueuedSynchronizer
- JUC:java/util/concurrent
- Synchronized和ReentrantLock的实现原理是不一致的,Synchronized是依靠java虚拟机的功能实现的。ReentrantLock则是有AQS这样一个背后大Boss在提供帮助啊!
基础原理



Thread.sleep、Object.wait、LockSupport.park 区别
实际上AQS是用WAITING来模拟JVM的BLOCKED状态;用Condition的await()来模拟Object的wait()——这也是Java线程间通信(java-thread-signaling)的两种方式
而线程的blocked状态,是这样一种状态:
A thread that is blocked waiting for a monitor lock is in this state.
可见它似乎是monitor锁机制的一个专属状态,在ReentrantLock和Condition配套使用时,就不会有这个状态,我们知道Condition提供了await()和signal(),当调用了signal()是不是唤醒了await()呢?其实没有,此时的状态还是waiting状态,线程也没有真正被唤醒,唤醒是在锁unlock()的时候。那么signal()做了什么呢?它把条件队列中挂起的线程调入同步队列中了。
这,不就和synchronized实现机制类似么?在waiting状态的线程原本处于_WaitSet中,当notify之后,线程也没有被唤醒,而是被“转移”到_EntryList中,唤醒线程是在锁释放后。
Condition调用signal()后线程仍然是waiting状态,那synchronized为什么要另起一个blocked状态?因为通过优化,synchronized实现的锁比想象中复杂,《Java 并发实战》写到,JVM实现阻塞可以采用自旋等待,或者通过操作系统挂起阻塞线程,而synchronized最初尝试获取锁时会采用自旋的方式,同时,它还提供了AQS实现锁没有的一些策略选择,这些做法的目的是为了优化,但此时的状态可以说是blocked状态,并不是waiting状态。

代码讲解
https://www.jianshu.com/p/3112bb0364a0
独占式
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 把获取资源失败的node放入AQS等待队列
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();//拿到node的前驱节点,赋值给p
if (p == head && tryAcquire(arg)) {
//如果p已经是头节点了,代表这个时候
//node是第二个节点,再次调用tryAcquire获取资源
setHead(node);//设置头节点
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&//判断此node是否可以被park
parkAndCheckInterrupt())//park
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
又是CAS自旋,首先拿到node的前驱节点,赋值给p,如果p已经是头节点了,代表这个时候node是第二个节点,再次尝试调用tryAcquire获取资源,如果成功,设置头节点为node,返回中断标记位,如果失败,先判断自己是否可以被park,如果可以的话,就park,等待unpark。
//叫醒Next正常的节点
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);
/* * 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)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
https://www.cnblogs.com/zyrblog/p/9866140.html

边栏推荐
猜你喜欢
随机推荐
安装MySQL的详细步骤
strlen 转义字符
sbl_init.asm-适合在编辑模式下看
Janus转发丢包导致音视频不同步原因分析
An abstract class, internal classes and interfaces
文件编辑器
CSDN spree -- college round table spree
[日常办公][杂项][vscode]tab space
【HIT-SC-MEMO3】哈工大2022软件构造 复习笔记3
通过socks5代理下载webrtc源码错误:curl: (7) Can't complete SOCKS5 connection xx.xx.xx.xx
彻底删除MySQL教程
(位操作符)按位与、按位或、按位异或
[Daily office][shell] Common code snippets
C语言数组的深度分析
arm learning-1-development board
2020-03-27
vs2017 redist 下载地址
FAREWARE ADDRESS
C语言对文件的操作(完整版)
MySQL索引








