如何基于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节点
-
thread
: 这个变量保存了当前Node
对象所代表的线程。当一个线程尝试获取锁但未能立即获得时,它会被封装在一个Node
对象中,并将其添加到同步队列中。 -
waitStatus
: 这个整型变量用于表示节点在队列中的等待状态。它有几种可能的值:-
CANCELLED: 表示线程已经取消了对同步状态的请求。
-
SIGNAL: 表示线程需要被唤醒(通常是因为其他线程释放了同步状态)。
-
CONDITION: 表示线程正在等待某个条件。
-
PROPAGATE: 表示下一次共享状态的释放应该传播到其他线程。
-
0: 初始状态,表示节点没有特定的状态。
waitStatus
值的变化是通过原子操作来保证线程安全。 -
-
nextWaiter
: 这个变量指向链表中的下一个Node
对象,AQS 可以通过这个链表来管理等待队列中的线程。
那么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实现了一个基本的不可重入锁