AQS是在面试的时候比较常问的内容,那么今天我们就来简单了解一下什么是AQS。
什么是AQS?
我们来简单说说什么是AQS,AQS其实是指Java中的AbstractQueuedSynchronizer类,这个类在java.util.concurrent.locks包下,是Java用来实现轻量级锁的类。
AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH队列是根据发明者三人名字的首字母命名的。CLH队列从逻辑上形成一个锁等待队列(实际无队列实例,仅通过节点间的关联来实现)从而实现加锁,CLH锁只支持按顺序加锁和解锁(FIFO先入先出),不支持重入,不支持中断。
AQS中还有一个state变量,用来判断当前是否为同步状态(通过CAS方法来改变state的值),0:资源空闲,当state变量大于0时表示资源正在锁定中,且state的值表示锁定资源的重入次数。
AQS的工作流程就是基于CLH虚拟队列,用volatile关键字修饰共享变量state,想要获取锁的线程通过CAS方法去改变state变量,成功则获取到锁,失败则进入等待队列中等待唤醒。
AQS采用了模板模式的设计方法
我们只需要按照模板创建自己的类,就可以生成一个自定义的同步器(锁)。
首先我们要遵守这两步:
1. 内部类同步器需要继承AbstractQueuedSynchronizer并重写指定的方法。
2. 外部类同步器需要实现Lock接口与Serializable接口提供对外服务。
我们去创建自定义同步器的时候只需要实现共享变量state的获取与释放即可,等待队列的维护都由AQS底层实现。
自定义同步器需要实现的方法:
方法名 | 作用 |
isHeldExclusively() | 该线程是否正在独占资源。只有用到condition才需要去实现它 |
tryAcquire(int) | 独占方式。尝试获取资源,成功则返回true,失败则返回false。 |
tryRelease(int) | 独占方式。尝试释放资源,成功则返回true,失败则返回false。 |
tryAcquireShared(int) | 共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。 |
tryReleaseShared(int) | 共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。 |
以ReentrantLock为例,(可重入独占式锁):state变量初始化为0,表示未锁定状态,A线程lock()时,会调用tryAcquire()独占锁并将state+1.之后其他线程再想tryAcquire的时候就会失败,直到A线程unlock()到state=0为止,其他线程才有机会获取该锁。A释放锁之前可以重复获取此锁(state累加),这就是可重入的概念。
注意:获取多少次锁就要释放多少次锁,保证state是能回到零态的。
以CountDownLatch为例,任务分N个子线程去执行,state就初始化为N,N个线程并行执行,每个线程执行完之后countDown()一次,state就会CAS减一。当N子线程全部执行完毕,state=0,会unpark()主调用线程,主调用线程就会从await()函数返回,继续之后的动作。
接下来我们尝试自定义实现一个同步器。
/**
* @description: 自定义同步器
* @author: Me
* @createDate: 2022/11/21 11:09
* @version: 1.0
*/
public class NonReentrantLock implements Lock, Serializable {
// 同步器实现时一般都将自定义同步器定义为内部类,供自己使用,并实现某个接口,向外提供服务,一般实现Lock接口
// 内部类的好处在于可以变相实现多继承
// 内部类会对同一个包下的其他类隐藏,内部类可以使用private关键字修饰
// 内部类可以访问外部类的全部字段与方法,外部类访问内部类需要创建内部类对象,内部类中不能有静态成员
private static class Sync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
assert arg == 1;
// cas方法,将变量0改为1
if (compareAndSetState(0,1)){
// 修改成功则上锁
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
assert arg == 1;
//如果state为0,则抛出异常
if (getState()==0){
throw new IllegalMonitorStateException();
}
// 如果state变量不为0,则释放锁
setExclusiveOwnerThread(null);
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
// 是否锁已经被持有
return getState() == 1;
}
//提供条件变量接口
public Condition newCondition(){
return new ConditionObject();
}
}
Sync sync = new Sync();
@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();
}
}
到这里AQS就简单介绍完啦,希望对小伙伴们有所帮助。