AQS(获取资源(独占模式)2)--队列同步器
1.获取资源(独占模式)
acquire(int)
首先讲解独占模式(Exclusive)下的获取/释放资源过程,其入口方法为:
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt(); }
tryAcquire(arg)为线程获取资源的方法函数,在AQS中定义如下:
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
注:该方法是空方法,且由protected修饰,说明该方法需要由子类即自定义同步器来实现。
acquire()方法至少执行一次tryAcquire(arg),若返回true,则acquire直接返回,
否则进入acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法。
实现的步骤:
第一步:addWriter()将当前线程加入到等待队列的尾部,并标记为独占模式;
第一步:acquireQueued()使线程在等待队列中获取资源,直到获取到资源返回,
若整个等待过程被中断过,则返回True,否则返回False。
第三步:如果线程在等待过程中被中断过,则先标记上,待获取到资源后再进行自我中断selfInterrupt(),将中断响应掉。
涉及的函数:
1.tryAcquire(int)
tryAcquire尝试以独占的模式获取资源,如果获取成功则返回True,否则直接返回False,
默认实现是抛出UnsupportedOperationException,具体实现由自定义扩展了AQS的同步器来完成。
2.addWaiter(Node)
addWaiter为当前线程以指定模式创建节点,并将其添加到等待队列的尾部,其源码为:
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 尝试将节点快速插入等待队列,若失败则执行常规插入(enq方法)
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 常规插入
enq(node);
return node;
}
再看enq(node)方法:
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
总结:可以看到,常规插入与快速插入相比:
第一点:常规插入是自旋过程(for(;;)),能够保证节点插入成功;
第二点:比快速插入多包含了1种情况,即当前等待队列为空时,需要初始化队列,
即将待插入节点设置为头结点,同时为尾节点(因为只有一个嘛)。
注:常规插入与快速插入均依赖于CAS,其实现依赖于unsafe类,具体代码如下:
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
unsafe中的cas操作均是native方法,由计算机CPU的cmpxchg指令来保证其原子性。

浙公网安备 33010602011771号