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{@Overrideprotected boolean tryAcquire(int arg) {assert arg == 1;// cas方法,将变量0改为1if (compareAndSetState(0,1)){// 修改成功则上锁setExclusiveOwnerThread(Thread.currentThread());return true;}return false;}@Overrideprotected boolean tryRelease(int arg) {assert arg == 1;//如果state为0,则抛出异常if (getState()==0){throw new IllegalMonitorStateException();}// 如果state变量不为0,则释放锁setExclusiveOwnerThread(null);setState(0);return true;}@Overrideprotected boolean isHeldExclusively() {// 是否锁已经被持有return getState() == 1;}//提供条件变量接口public Condition newCondition(){return new ConditionObject();}}Sync sync = new Sync();@Overridepublic void lock() {sync.acquire(1);}@Overridepublic void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}@Overridepublic boolean tryLock() {return sync.tryAcquire(1);}@Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(time));}@Overridepublic void unlock() {sync.release(1);}@Overridepublic Condition newCondition() {return sync.newCondition();}
}
到这里AQS就简单介绍完啦,希望对小伙伴们有所帮助。