AbstractQueuedSynchronizer
AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore等都是基于AQS的。
核心思想
AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果请求的共享资源被占用,那么就需要一套线程阻塞以及被唤醒时锁分配机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
它维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。state的访问方式有三种
- getState():获取State的值
- setState(): 设置State的值
- compareAndSetState():使用CAS方式更新State
AQS定义两种资源共享方式
- Exclusive(独占,只有一个线程能执行,如ReentrantLock)
- Share(共享,多个线程可以同时执行,例如Semaphore/CountDownLatch)
不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现共享资源state的获取和释放方式即可,至于具体线程等待的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:
- isHeldExclusicely(): 该线程是否正在独占资源,只有用到condition才需要去实现它。
- tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败返回false。
- tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
- tryAcquireShared(int):共享方式。尝试获取资源,负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
- tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待节点返回true,否则返回false。
ReentrantLock的实现
state初始化为0,表示未锁定的一个状态,当一个线程lock()时,会调用tryAcquier()独占锁并将state+1。之后其他线程再想tryAcquire()的时候就会失败,知道该线程unlock()到state=0为止,其它线程才有机会获取该锁。当此线程释放锁前,自己也是可以充入获取此锁(state累加,这就是一个可重入的概念),但是需要注意获取多少次锁就要释放多少次锁,这个就是可重入的概念
CountDownLatch的实现
任务分为N个子线程去执行,state就初始化为N,N个线程并行执行,每个线程执行完countDown()一次,state就会CAS减一。当N子线程全部执行完毕,state=0,会unpark()主调用线程,主调用线程就会从await()函数返回,继续之前的动作。
一般来说,自定义同步器要么是独占方法,要么是共享方式。但是AQS也支持自定义同步器实现独占和共享两种方式,例如ReentrantReadWriteLock。
在acquire()和acquireShared()两种方式下,线程在等待队列中都是忽略中断的,acquireInterruptibly() acquireSharedInterruptibly()是支持响应中断的,他们相应的源码都相差不大。
CountDownLatch源码
同步类在实现时一般都将自定义同步器(sync)定义为内部类,供自己使用;而sync只用实现资源state的获取-释放方式tryAcquire-tryRelelase,至于线程的排队、等待、唤醒等,上层的AQS都已经实现好了,我们不用关心。
感谢各位大佬的❤️关注+点赞❤️,原创不易,鼓励笔者创作更好的文章