-
CONDITION(-2): 线程正在等待状态 这个状态只在condition阻塞队列中设置。
-
PROPAGATE(-3):只设置在头节点,代表需要继续无条件唤醒后继,只应用于共享模式。
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
private static final long serialVersionUID = 7373984972572414691L;
/**
-
Wait queue node class.
-
The wait queue is a variant of a "CLH" (Craig, Landin, and
-
Hagersten) lock queue. CLH locks are normally used for
-
spinlocks.
-
To enqueue into a CLH lock, you atomically splice it in as new
-
tail. To dequeue, you just set the head field.
-
+------+ prev +-----+ +-----+
-
head | | <---- | | <---- | | tail
-
+------+ +-----+ +-----+
//双向队列
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
//共享一个节点对象
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
//独占节点
static final Node EXCLUSIVE = null;
/** waitStatus value to indicate thread has cancelled */
//取消状态的节点会被剔除
static final int CANCELLED = 1;
/** waitStatus value to indicate successor’s thread needs unparking */
//预示后继节点的线程需要被唤醒
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
//线程正在等待状态 这个状态只在condition await时设置
static final int CONDITION = -2;
/**
-
waitStatus value to indicate the next acquireShared should
-
unconditionally propagate
*/
static final int PROPAGATE = -3;
//节点状态-节点在获取锁和释放锁的状态流程
volatile int waitStatus;
//有前继指针,说明是双向队列
volatile Node prev;
volatile Node next;
//记录阻塞的线程
volatile Thread thread;
//condition中记录下一个节点,Lock中记录当前的node是独占node还是共享node
Node nextWaiter;
/**
- Returns true if node is waiting in shared mode.
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
/**
-
获取前继节点
-
Returns previous node, or throws NullPointerException if null.
-
@return the predecessor of this node
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
//node的上一个节点为空,直接抛异常
if (p == null)
throw new NullPointerException();
else
return p;
}
…
}
//队列头指针
private transient volatile Node head;
//队列尾指针
private transient volatile Node tail;
//The synchronization state.
private volatile int state;
…
}
3、一个state一个exclusiveOwnerThread
state变量用于记录锁的状态,state=0代表锁空闲,state>0代表有线程持有锁,独占模式下state数值代表锁重入的次数;共享模式下,因为可以多个线程持有锁,所以state不能作为重入次数的记录,需要单独实现。
AbstractQueuedSynchronizer
还继承了AbstractOwnableSynchronizer
,主要继承了AbstractOwnableSynchronizer
独占模式下记录锁所有者线程的功能。
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
private static final long serialVersionUID = 3737899427754241961L;
protected AbstractOwnableSynchronizer() { }
/**
- The current owner of exclusive mode synchronization.
*/
private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}
4、何时入队列,出队列呢?
-
入队列,线程获取锁失败,入队列将新节点加到tail后面,然后对tail进行CAS操作,将tail指针后移到新节点上。
-
出队列,锁释放唤醒head的后继节点,head的后继节点从阻塞中醒来,开始抢锁,获取锁成功,此时head指针向后移一个位置,原先head的后继节点成为新的head。
(1)入队列
-
新建一个节点。
-
判断tail不为空,新节点CAS排到队列尾部,tail指针指向新节点。
-
若tail为空或者CAS排到队列尾部失败,进入自旋enq。
-
判断tail是否为空,为空则初始化,初始化需要CAS设置head,此时head和tail都指向一个空节点(thread=null)。
-
初始化完成后再循环一次,CAS将新节点排到队列尾部,失败则继续自旋。
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节点的上一个节点是tail
node.prev = pred;
//cas设置tail指针指向node
if (compareAndSetTail(pred, node)) {
pred.next = node;
//mode进入尾部成功,返回
return node;
}
}
//pred = null 还没初始化
//or mode没有插入链接尾部,自旋cas到尾部
enq(node);
return node;
}
//cas自旋入队列->尾部
private Node enq(final Node node) {
for (;😉 {
Node t = tail;
if (t == null) { // Must initialize
//先初始化
if (compareAndSetHead(new Node()))
tail = head;
} else {
//自旋 cas node到链表尾部
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
(2)出队列
出队列就很简单,就是一个换头的动作。出队换头不需要CAS,对于独占锁只有一个线程获取锁所以是安全的;对于共享锁,虽然可以多个线程获取锁,但是共享锁的唤醒出队操作是顺序进行的,所以出队换头也是安全的。
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
5、AQS中模板方法设计模式
AQS作为一个基础组件,将队列操作封装成一个个模板,其中获取锁、释放锁的动作交由子类自主实现。
(1)独占模式获取锁
独占模式获取锁失败后,进入队列的操作是可复用的,至于在没有入队列前怎么获取锁全由子类决定,所以子类只需要实现tryAcquire
方法。
public final void acquire(int arg) {
//若没有抢到锁,则进入同步队列
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//自己中断自己
selfInterrupt();
}
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
(2)独占模式释放锁
独占模式下释放锁,唤醒后继的动作也是可复用的,至于如何释放锁,由子类自行实现tryRelease
方法。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//释放锁成功后唤醒后继节点
unparkSuccessor(h);
return true;
}
return false;
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
(3)共享模式获取锁
共享模式获取锁和独占模式获取锁类似,进入队列的操作可以复用,子类只需要实现tryAcquireShared
即可。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的增加文章的篇幅,减少文章的可读性
Java面试宝典2021版
最常见Java面试题解析(2021最新版)
2021企业Java面试题精选
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的增加文章的篇幅,减少文章的可读性
Java面试宝典2021版
[外链图片转存中…(img-mYmIiqjf-1713307272837)]
[外链图片转存中…(img-gTT9Ukxp-1713307272837)]
最常见Java面试题解析(2021最新版)
[外链图片转存中…(img-envVP1Fv-1713307272837)]
[外链图片转存中…(img-shposmVK-1713307272837)]
2021企业Java面试题精选
[外链图片转存中…(img-QIcbvMxZ-1713307272838)]
[外链图片转存中…(img-8WcYuAGr-1713307272838)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!