AQS及派生类
一、AQS
AQS名字为一个抽象队列同步器,是一个抽象类。它提供了一个框架,ReentrantLock、Semaphore和CountDownLatch都是基于AQS实现的。
首先,AQS中提供了一个由volatile修饰的int类型的共享资源,就是state变量。在ReentrantLock的实现类中,state用来表示锁的占有情况,包括可重入计数,当state的值为0的时候,标识该锁不被任何线程所占有。
其次,AQS还要维护一个FIFO队列,是一个双向链表,用来存放等待的线程。
最后,AQS有一个获取锁(tryAcquire)和释放锁(tryRelease)的方法,需要由实现类去重写该方法。
二、ReentrantLock
ReentrantLock的实现原理主要依赖于AQS框架。AQS中提供了一个由volatile修饰的state变量。在ReentrantLock的实现类中,state用来表示锁的占有情况,当state的值为0的时候,标识该锁不被任何线程所占有。其次,AQS还要维护一个FIFO队列,是一个双向链表,用来存放等待的线程。
(1)ReentrantLock支持可重入性,同一个线程可以多次获得同一把锁。这是通过内部的 holdCount计数来实现的。holdCount初始值为0,获取一次锁holdCount+1,释放一次锁holdCount-1。
(2)ReentrantLock支持设置超时时间,即等待一定时间后如果还未获得锁,则放弃锁的获取。
(3)ReentrantLock实现了可中断性,这意味着线程在等待锁的过程中,可以被其他线程中断而提前结束等待。在底层,ReentrantLock使用了与LockSupport.park()和 LockSupport.unpark()机制来实现可中断性。
(4)ReentrantLock支持公平锁和非公平锁,默认是非公平锁,new ReentrantLock(true)则是公平锁。
(5)多个条件变量:ReentrantLock支持多个条件变量,每个条件变量可以只与一个 ReentrantLock关联。
三、CountDownLatch
参考视频:动画演示CountDownLatch的工作原理_哔哩哔哩_bilibili
CountDownLatch允许count个线程阻塞在一个地方,直至所有线程的任务都执行完毕。
CountDownLatch基于AQS,AQS有一个state变量,在CountDownLatch中代表计数器count,计数器可以被多个线程共享。计数器要通过构造函数赋值,比如new CountDownLatch(3),计数器的初始值就是3。
CountDownLatch有两个方法,await()和countDown()。线程调用await()方法会阻塞,也就是放到AQS里的阻塞队列,直到计数器减到0。在其他线程完成任务后,会调用countDown()方法使得计数器减一。计数器减到0时,阻塞队列中等待的线程都会被激活,因此原本调用await()方法的线程会继续执行。
CountDownLatch是一次性的,计数器减到0后就不能重置了,如果需要重复使用,可以使用CyclicBarrier。
四、Semaphore
参考视频:动画演示Semaphore的核心原理_哔哩哔哩_bilibili
Semaphore信号量主要是用于控制多个线程对有限个共享资源的访问权限。
Semaphore基于AQS,AQS有一个state变量,在Semaphore中代表共享资源的个数,或叫许可证的数量permits。
当线程想要访问资源时,它必须先执行semaphore.acquire()获取一个许可证,如果许可可用,则线程继续执行,否则线程将被放入AQS的阻塞队列。当线程完成对资源的访问后,它会执行semaphore.release()用来归还许可证,并在AQS队列中唤醒一个线程,允许该线程获取许可并访问资源。唤醒一个线程可以是公平的方式,也可以是非公平的方式。