兄得我今天分享一次有关AQS的面试经历,如有雷同,咱们可能是同一个面试官,瓜子、板凳、啤酒、花生米备好了,咱们正式开始。
AQS是队列同步器AbstractQueueSynchronizer的简写,它是用来构建锁和其他同步组件的基础框架,它定义了一个全局的int 型的state变量,通过内置的FIFO(先进先出)队列来完成资源竞争排队的工作。
模版设计模式
方法名称 | 方法说明 |
---|---|
Acquire() | 独占锁获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,否则,将会进入同步队列等待,该方法将会调用重写的tryAccquire()方法 |
acquireShared() | 获取共享锁,如果当前线程没有获取到共享锁,则会进入到同步等待队列,,他与独占锁不同的是,共享锁能被多个线程同时占有 |
release | 释放同步状态,并且通知同步器,唤醒等待队列中第一个节点中包含的线程。 |
releaseShare | 释放同步状态 |
内心OS:幸好我提前刷了面试题,不然就被卡在这里了。首先来张图,来镇镇场子
上面的这个流程,细心的同学应该会发现一个问题,设置首尾节点的时候会不会发生线程安全问题呢?如果会的话应该怎么做呢?
接下来,我们看下它们是怎么做的:
我们先分析下,为什么设置尾节点的时候会出现线程安全呢?
同步器如何设置首节点的时候,是不是也要用cas来保证线程安全呢?
当时第一反应,当然咯,但是在大脑飞速运转之后,回想起来昨天晚上刷面试题的时候,好像刷到了这个题,然后推口而出:当然不会咯,面试官的笑容逐渐消失,当即问我,为什么不会?我是这样回答的:
独占式同步状态的获取与释放的源码你有了解么?
“我内心的独白是,这个我早已了然于胸”
public final void acquire(int arg) {
if(!tryAcquire(arg)
&&acquireQueued(addWaiter(Node.EXCLUSIVE),arg))
seleInterrupt();
}
}
哈哈,虽然只有几行代码,但是它却完成了同步锁获取的整个过程,你还别不信,我来和你娓娓道来
private Node addWaiter(Node node) {
Node node = new Node(Thread.currentThread(),mode);
// 快速尝试在尾部添加
Node pred = tail;
if(pred!=null){
node.prev = pred;
if(compareAndSetTail(pred,node)) {
pred.next = node;
return node;
}
}
enq(node);
}
private Node enq(final Node node) {
for(;;){
Node t = tail;
if(t==null){
if(compareAndSetHead(new Node())){
tail = node;
}
}else{
node.prev = t;
if(compareAndSetTail(t,node)){
t.next = node;
return t;
}
}
上面两段代码第一次看会有点晕,乐哉也是的,当时结合上面分析过的流程来看,感觉就会清晰很多,我直接画个思维导图吧
我们歇一会,继续分析acquireQueued 方法,看看它里面都做了什么呢?
final boolean acquireQueued(final Node node,int arg){
boolean failed = true;
try{
boolean interrupted = false;
for(;;){
final Node p = node.predcessor();
if(p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
return interrupted;
}
}
}
}
分析上面的代码片段,可以得出acquireQueued内部使用自旋的方式获取同步状态,并且只有 当前节点的前驱节点是头节点,才会尝试调用tryAcquire 获取同步状态,否则继续自旋。面试官看到我说到了这里,就又插了一句说:
为什么只能其前驱节点是头节点,才会尝试获取同步状态呢?
真想来一句,这不快要说了么!!(暴脾气)
为什么只能其前驱节点是头节点,才会尝试获取同步
当拥有同步状态的线程释放同步状态的时候,会唤醒其后继节点。为了维护FIFO原则,其后继节点被唤醒后需要检查自己的前驱节点是不是头节点,这样才能保证FIFO原则。独占锁的释放我就不说了,这里用一个流程图来汇总下我说内容
老师,我给你画个图吧,画画个北北,画画个北北。。。突然唱起来了,面试官一脸瞪着我,好尴尬啊。
面试官回答说:这个是他们概念上的区别
从面试官的回答来看,他其实就是想试探下你有没有深入的去了解AQS的源码,因为我是看过的啊,于是我又继续和他侃大山了。