1、Java 并发包下很多 API 都是基于 AQS 来实现的加锁和释放锁等功能的。
⽐如说 ReentrantLock、ReentrantReadWriteLock 底层都是基于 AQS 来实现的。
AQS 对象就是实现加锁和释放锁的关键性的核⼼组件。
2、ReentrantLock加锁和释放锁的原理
ReentrantLock lock = new ReentrantLock();
//加锁
lock.lock();
//释放锁
lock.unlock();
①.AQS 对象内部有⼀个核⼼的变量叫做 state,是 int 类型的,代表了加锁的状态。 初始状态下,这个 state 的值是 0
②.AQS 内部还有⼀个关键变量,⽤来记录当前加锁的是哪个线程,初始化状态下,这 个变量是 null
③.线程 1 跑过来调⽤ ReentrantLock 的 lock() ⽅法尝试进⾏加锁,这个加锁的过程,直接就是 ⽤ CAS 操作将 state 值从 0 变为 1,⼀旦线程 1 加锁成功了之后,就可以设置当前加锁线程是⾃⼰
④.每次线程 1 可重⼊加锁⼀次,会判断⼀下当前加锁线程就是⾃⼰,那么他⾃⼰就可以可重 ⼊多次加锁,每次加锁就是把 state 的值给累加 1
⑤.线程 2 过来state 的值不是 0 ,所以 CAS 操作将 state 从 0 变为 1 的过程会失败,会将⾃⼰放⼊ AQS 中的⼀个等待队列
⑥.线程 1 在执⾏完⾃⼰的业务逻辑代码之后,就会释放锁!他释放锁的过程⾮常的简单, 就是将 AQS 内的 state 变量的值递减 1,如果 state 值为 0,则彻底释放锁,会将 “加锁线程” 变 量也设置为 null
⑦.接下来,会从等待队列的队头唤醒线程 2 重新尝试加锁。 线程 2 这时还是⽤ CAS 操作将 state 从 0 变为 1,成功之后代表加锁成功,就会将 state 设置为 1。 还要把 “加锁线程” 设置为线程 2 ⾃⼰,同时线程 2 ⾃⼰就从等待队列中出队了。
总结:AQS 就是⼀个并发包的基础组件,⽤来实现各种锁,各种同步组件的。它包含 了 state 变量、加锁线程、等待队列等并发中的核⼼组件
3、公平锁与非公平锁
①非公平锁:
线程 1 刚释放锁,线程 3直接就跑过来抢先加锁了。导致线程 2 加锁失败继续留在等待队列⾥不断的等着,等着线程 3 释放锁之后,再来唤醒⾃⼰。
②公平锁:
等待队列中,线程 3 会按照公平原则直接进⼊队列尾部进⾏排队。