图2-5-9-5 独占模式与共享模式
如何通过state实现独占模式和共享模式?在此之前先了解AQS框架中相关的getState、setState、compareAndSetState三个操作state的基本方法,前两个方法是普通的获取设置方法,其必须保证不存在数据竞争的情况下使用,compareAndSetState方法则提供了CAS方式的硬件级别的原子更新。两种模式就是通过这些方法对state操作实现不同同步模式,下面给出最简单的实现。
独占模式
public boolean tryAcquire(int acquires) {
if (compareAndSetState(0, 1)) {
return true;
}
return false;
}
protected boolean tryRelease(int releases) {
setState(0);
return true;
}
多条线程通过tryAcquire尝试把state变量改为1,由于CAS算法的保证,最终有且仅有一条线程成功修改state,修改成功的线程代表获取锁成功,将拥有往下执行的权利,进入管道。当执行完毕退出管道时执行tryRelease尝试把state变量改为0,让出管道,此处由于不存在线程竞争所以可直接使用setState,接着其他未通过的线程继续重复尝试。
共享模式
public int tryAcquireShared(int interval) {
for (;;) {
int current = getState();
int newCount = current - 1;
if (newCount < 0 || compareAndSetState(current, newCount)) {
return newCount;
}
}
}
public boolean tryReleaseShared(int interval) {
for (;;) {
int current = getState();
int newCount = current + 1;
if (compareAndSetState(current, newCount)) {
return true;
}
}
}
与独占模式不同的是对state的管理及判断条件,独占模式state的值只能为0或1,而共享模式的state是可以被出事换成任意整数,一般初始值表示提供一个同时n条线程通过的管道宽度,这样一来,多条线程通过tryAcquireShared尝试将state的值减去1,成功修改state后就返回新值,只有当新值大于等于0才表示获取锁成功,拥有往下执行的权利,进入管道。在执行完毕时线程将调用tryReleaseShared尝试修改state值使之增加1,表示我已经执行完了并让出管道的通道供后面线程使用,需要说明的是与独占模式不同,由于可能存在多条线程并发释放锁,所以此处必须使用基于CAS算法的修改方法,修改成功后其他线程便可继续竞争锁。
ASQ框架提供了对同步状态state的基本操作,了解了两种模式对state操作开发者可能很*地自定义自己的同步器。实际中AQS框架在提供state状态管理接口的同时也将维护等待队列的工作,两项工作被封装成一个模板,规定了工作流程,工作流程包括什么条件下加入等待队列、什么条件移除等待节点、如何操作等待队列、需不需要阻塞、支不支持中断等等,对外仅仅提供state状态操作接口供开发者自定义,而队列的维护工作已经绑定在模板中,无需你自己动手。