写在前面
在上一篇AQS学习笔记(二)AbstractQueuedSynchronizer概览中,已经对AQS进行了一些通读,有了最基本的概念掌握。现在开始尝试着使用AQS
不过我们也已经知道了,Doug Lea告诉我们AQS应该被定义为非公开的内部类,在类的内部封闭地的实现外部需要的某些属性。官方给出了一个示例实现了一个锁,即便AQS不仅仅可以被用来做成锁的组件,也可以用作其他的,但从学习角度上来讲,我们就先来研究一下官方用例。
不过在这之前,先大致了解一下Lock
接口的使用
Lock接口
概览
Lock接口同样是在J.U.C下一个叫locks包中的一个接口,作者是Doug Lea。官方对Lock
接口的说明是,其提供了一些比synchronized
更易扩展更易维护锁操作的方法。也可以使用Condition
来支持多关联。
方法很简单,如下
方法名及参数返回值 | 说明 |
---|---|
lock(): void | 获取锁。如果该锁不可用,当前线程会被阻塞休眠直到获取到锁 |
lockInterruptibly(): void | 与lock()不同的是,当前线程在阻塞时可接受其他线程的中断 |
tryLock(): boolean | 获取锁,在获取锁失败时会返回false而不是阻塞,成功时返回true |
tryLock(long time, TimeUnit unit): boolearn | 获取锁,失败时线程阻塞,直到获取到锁;或者被中断;或者超时退出 |
unlock():void | 释放锁 |
newCondition(): Condition | 获取一个新绑定到该锁上的Condition实例 |
案例
互斥锁
官方Demo是一个独占的互斥锁,仅有一个线程能在同一时间内可获取锁,先把框架搭好
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class MyMutex implements Lock {
private static class MySync extends AbstractQueuedSynchronizer {
}
@Override
public void lock() {
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public void unlock() {
}
@Override
public Condition newCondition() {
return null;
}
}
此时我们需要对AQS的行为进行定义
根据需求,要求同一时刻仅有单线程独占锁,所以将MySync
的同步状态值总量定义1,同步值除了0和1都是非法状态。也因为是独占式的锁,所以需要重写AQS的tryAcquire()
方法、tryRelease()
方法和isHeldExclusively()
方法;独占模式还需要使用Condition
,所以对外提供获取其内部类ConditionObject
的方法
对同步状态值的操作,使用getState()
、setState(int newState)
和compareAndSetState(int expect, int update)
所以有
private static class MySync extends AbstractQueuedSynchronizer {
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
@Override
protected boolean tryAcquire(int acquires) {
assert acquires == 1; // 在这里使用断言,是因为其他值非法
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int releases) {
assert releases == 1; // 在这里使用断言,是因为其他值非法
if(getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
Condition newCondition() {
return new ConditionObject();
}
}
其中代码使用了一个setExclusiveOwnerThread
的独占式地方法,此方法来源于AQS的抽象父类AbstractOwnableSynchronizer
,是Doug Lea在JDK1.6以后添加的
其内部维护了一个transient
的线程成员变量exclusiveOwnerThread
,除了set方法为其赋值以外,也提供了getExclusiveOwnerThread
取出该变量供子类使用
官方示例中还有一个用于反序列化的readObject
方法,这里就不再展示了
剩下的就是补充Lock的外层方法,经简单扩展后,代码为
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class MyMutex implements Lock, Serializable {
private static class MySync extends AbstractQueuedSynchronizer {
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
@Override
protected boolean tryAcquire(int acquires) {
assert acquires == 1; // 在这里使用断言,是因为其他值非法
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int releases) {
assert releases == 1; // 在这里使用断言,是因为其他值非法
if(getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
Condition newCondition() {
return new ConditionObject();
}
}
private final MySync sync = new MySync();
// 重写Lock接口的方法
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
@Override
public void unlock() {
sync.tryRelease(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
// 额外提供的方法
public boolean isLocked() {
return sync.isHeldExclusively();
}
public boolean hasQueuedThreads() {
return sync.hasQueuedThreads(); // 是否有等待线程
}
}
至此,官方用例就完成了。实现了一个简单的互斥锁
双线程共享锁
这个demo是《并发编程》里提供的一个共享锁例子。同一时刻最多允许有两个线程持有,所以同步状态最大值设置为2,每获取一次则状态值减一,0表示已经有两个线程持有;不支持重入。
该锁的取名为TwinsLock
,实现过程与上个例子类似,先搭建骨架,再写AQS子类的实现,最后提供对外暴露的方法。