一、使线程进入等待状态和唤醒状态的三种方法
1、使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程,需配合synchronized关键字使用
2、使用JUC包中的Condition的await()方法让线程等待,使用signal()方法唤醒线程
3、LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程
其中,LockSupport实现了线程的等待唤醒机制,可以认为是wait/notify的改良加强版,其方法park()和unpark()分别实现了线程的阻塞和解除阻塞
抢到资源的线程,直接使用资源来处理业务逻辑,抢不到资源的线程必然涉及到一种排队等候机制。抢占资源失败的线程继续去等待(类似银行业务办理窗口都满了,暂时没有受理窗口的顾客只能去后可取排队等候),但等候线程仍然保留获取锁的可能且获取锁的流程仍然在继续(候客区的顾客也在等着叫号,轮到了再去受理窗口办理业务)。
既然说到了排队等候机制,那么就一定会有某种形式的队列,这样的队列是什么样的数据结构呢?
如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁的分配。这个机制主要是使用的CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这个队列就是AQS的抽象表现。它将请求共享资源的线程封装成队列的节点(Node),通过CAS、自旋以及LockSupport.park()的方式,维护state变量的状态,使并发达到同步的控制效果。
二、ReentrantLock核心类图
ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore的核心功能都是依赖AbstractQueueSynchronizer(抽象队列同步器,简称AQS)实现的,以ReentrantLock为例看一下其核心类图如下:
三、AQS基本结构
AQS是用来构建锁或者其他同步器组件的重量级基础框架及整个JUC体系的基石,通过内置的FIFO队列来完成资源的获取和线程的排队工作,并通过一个int类型的变量表示持有锁的状态。
CLH队列(FIFO)是一个单向链表,AQS中的队列是CLH变体的虚拟双向队列FIFO
四、AQS工作原理