队列同步器AbstractQueuedSynchronizer(以下简称同步器),是用来构建锁或者其他同步组件的基础框架。
1、它使用了一个int成员变量表示同步状态。
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
2、通过内置的FIFO双向队列来完成获取锁线程的排队工作。
同步器包含两个节点类型的应用,一个指向头节点,一个指向尾节点,未获取到锁的线程会创建节点线程安全(compareAndSetTail)的加入队列尾部。同步队列遵循FIFO,首节点是获取同步状态成功的节点。
通过节点的方式(Node),创建队列。
/**
* CAS head field. Used only by enq.
*/
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
/**
* CAS tail field. Used only by enq.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
未获取到锁的线程将创建一个节点,设置到尾节点。如下图所示:
首节点的线程在释放锁时,将会唤醒后继节点。而后继节点将会在获取锁成功时将自己设置为首节点。如下图所示:
3、独占式/共享式锁获取
独占式:有且只有一个线程能获取到锁,如:ReentrantLock;
共享式:可以多个线程同时获取到锁,如:CountDownLatch;
独占式
每个节点自旋观察自己的前一节点是不是Header节点,如果是,就去尝试获取锁。
独占式锁获取流程:
共享式:
共享式与独占式的区别:
tryAcquireShared
protected int tryAcquireShared(int arg)试图在共享模式下获取对象状态。此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它。
tryReleaseShared
protected boolean tryReleaseShared(int arg)试图设置状态来反映共享模式下的一个释放。
此方法总是由正在执行释放的线程调用。
tryAcquire
protected boolean tryAcquire(int arg)试图在独占模式下获取对象状态。此方法应该查询是否允许它在独占模式下获取对象状态,如果允许,则获取它。
tryRelease
protected boolean tryRelease(int arg)试图设置状态来反映独占模式下的一个释放。
此方法总是由正在执行释放的线程调用。
节点Node 组成队列
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
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;
}
}
}
}