Fork me on GitHub

Java并发编程实战———AbstractQueuedSynchronizer

在基于AQS构建的同步器类中,最基本的操作包括各种形式的获取操作和释放操作。获取操作是一种依赖状态的操作,并且通常会阻塞。当使用锁或信号量时,“获取”操作的含义即获取的是锁或者许可,并且调用者可能会一直等待直到同步器类处于可被获取的状态。在使用CountDownLatch时,“获取”操作意味着“等待并直到闭锁到达结束状态”,而在使用FutureTask时,则意味着“等待并直到任务已经完成”。“释放”并不是一个可阻塞的操作,当执行“释放”操作时,所有在请求时被阻塞的线程都会开始执行。

如果一个类想成为状态依赖的类,那么它必须拥有一些状态。AQS负责管理同步器类中的状态,它管理了一个整数状态信息,可以通过getStatesetState以及compareAndSetStateprotected类型方法来进行操作。这个整数可以用于表示任意状态。例如,ReentrantLock用它来表示所有者线程已经重复获取该锁的次数,Semaphore用它来表示剩余的许可数量,FutureTask用它来表示任务的状态(尚未开始、正在运行、已完成以及已取消)。在同步器类中还可以自行管理一些额外的状态变量,例如,ReentrantLock保存了锁的当前所有者的信息,这样就能区分某个获取操作是重入的还是竞争的。

一个获取操作包括两部分。首先,同步器类判断当前状态是否允许获得操作,如果是,则允许线程执行,否则获取操作将阻塞或失败。其次,就是更新同步器的状态,获取同步器的某个线程可能会对其他线程能否也获取该同步器造成影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// AQS中获取操作和释放操作的标准形式
boolean acquire() throws InterruptedException {
while (当前状态不允许获取操作) {
if (需要阻塞获取请求) {
如果当前线程不在队列中,则将其插入队列
阻塞当前线程
} else {
返回失败
}
}
可能更新同步器的状态
如果线程位于队列中,则将其移除队列
返回成功
}

void release() {
更新同步器的状态
if (新的状态允许某个被阻塞的线程获取成功) {
解除队列中一个或多个线程的阻塞状态
}
}

如果某个同步器支持独占的获取操作,那么需要实现一些保护方法,包括tryAcquiretryReleaseisHeldExclusively等,而对于共享获取的同步器,则应该实现tryAcquireSharedtryReleaseShared等方法。AQS中的accquireacquireSharedreleasereleaseShared等方法都将调用这些方法在子类中带有try的版本来判断某个操作是否能执行。在同步器的子类中,可以根据其获取操作和释放操作的语义,使用getStatesetSate以及compareAndSetSate来检查和更新状态,并通过返回的状态值来告知基类“获取”或“释放”同步器的操作是否成功。

求鼓励,求支持!