如何基于AQS实现一个自定义锁
AQS是什么
AQS全称是 AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架
AbstractQueuedSynchronizer的重要属性有
private volatile int state;
private transient volatile Node head;
private transient volatile Node tail;
可以看到AbstractQueuedSynchronizer的内部有一个头节点Node和一个尾节点Node
那么Node节点是什么呢,是AbstractQueuedSynchronizer的一个内部类
volatile Thread thread;
volatile Node prev;
volatile Node next;
volatile int waitStatus;
Node nextWaiter;
可以看到Node节点是一个双向链表,除此之外还维护了一个thread线程和waitStatus变量以及nextWaiter节点
那么AbstractQueuedSynchronizer内部有哪些方法呢
-
acquire(int arg): 尝试获取一个单元的同步状态。如果获取成功,返回 true;如果失败,则将线程添加到等待队列中,并阻塞当前线程,直到再次尝试获取成功。
-
acquireInterruptibly(int arg): 类似于 acquire,但线程可以被中断。
-
tryAcquire(int arg): 尝试非阻塞地获取同步状态。默认实现总是返回 false,需要子类重写以实现特定的获取行为。
-
tryRelease(int arg): 尝试释放一个单元的同步状态。默认实现总是返回 false,需要子类重写以实现特定的释放行为。
-
acquireShared(int arg): 用于实现共享模式的同步器,如 ReadWriteLock。
-
acquireSharedInterruptibly(int arg): 与 acquireShared 类似,但线程可以被中断。
-
tryAcquireShared(int arg): 尝试非阻塞地获取共享模式的同步状态。
-
tryReleaseShared(int arg)尝试释放共享模式的同步状态。
-
release(int arg): 释放同步状态。如果所有线程都被唤醒,这个方法将返回 true。
-
releaseShared(int arg): 用于共享模式的同步器的释放操作。
-
hasQueuedThreads(): 返回是否有线程正在等待获取同步状态。
-
hasContended(): 返回是否有线程曾经争用过这个同步器。
-
getFirstQueuedThread(): 返回等待队列中的第一个线程,如果没有线程等待,则返回 null。
-
getQueuedThreads(): 返回一个包含所有等待线程的集合。
-
getExclusiveQueuedThreads(): 返回一个包含所有等待获取独占访问权的线程的集合。
-
getQueueLength(): 返回等待队列中的线程数。
-
isHeldExclusively()默认实现中,它检查同步状态是否为零,如果为零,则表示当前没有线程持有独占锁
子类可以重写的方法主要有以下方法
tryAcquire
tryRelease
tryAcquireShared
tryReleaseShared
isHeldExclusively
实现一个同步器类
final class MySync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int acquires) {
if (acquires == 1){
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
}
return false;
}
@Override
protected boolean tryRelease(int acquires) {
if(acquires == 1) {
if(getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
return false;
}
protected Condition newCondition() {
return new ConditionObject();
}
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
}
自定义同步器类继承自AbstractQueuedSynchronizer,这里重写了tryAcquire()、tryRelease()和isHeldExclusively()方法,并提供了一个newCondition()方法来获得条件变量,ConditionObject是AbstractQueuedSynchronizer的一个内部类,用以实现条件变量
这个自定义同步器实现了一个基本的锁机制,其中:
-
一个线程可以通过 tryAcquire 方法尝试获取锁。
-
持有锁的线程可以通过 tryRelease 方法释放锁。
-
线程可以使用 Condition 对象来等待或等待直到某个条件成立。
实现一个自定义锁
有了自定义同步器 ,就能更轻易实现一个功能完备的自定义锁
实现一个锁,首先要实现Lock接口,并实现其中的抽象方法,在其中定义一个自定义同步器成员变量
class MyLock implements Lock {
static MySync sync = new MySync();
@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 time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
// 释放锁
public void unlock() {
sync.release(1);
}
@Override
// 生成条件变量
public Condition newCondition() {
return sync.newCondition();
}
}
-
lock(): 此方法调用 MySync 类的 acquire(1) 方法,尝试获取锁。如果锁已被其他线程持有,则当前线程将被放入等待队列中,直到能够获取锁。
-
lockInterruptibly(): 此方法调用 MySync 类的 acquireInterruptibly(1) 方法,尝试获取锁。与 lock() 方法不同,如果线程在获取锁时被中断,将会抛出 InterruptedException。
-
tryLock(): 此方法调用 MySync 类的 tryAcquire(1) 方法,尝试立即获取锁,如果成功则返回 true,如果失败则返回 false,不会阻塞或进入等待队列。
-
tryLock(long time, TimeUnit unit): 此方法调用 MySync 类的 tryAcquireNanos(1, unit.toNanos(time)) 方法,尝试在给定的等待时间内获取锁。如果超时,则返回 false。如果线程在尝试期间被中断,将抛出 InterruptedException。
-
unlock(): 此方法调用 MySync 类的 release(1) 方法,释放当前线程持有的锁。
-
newCondition(): 此方法调用 MySync 类的 newCondition() 方法,创建一个新的 Condition 对象,允许线程在锁上等待或等待直到某个条件成立。
总结
本文先详细讲解了AQS,随后基于AQS实现了一个基本的不可重入锁