AQS 独占模式:深入理解 JUC 中的同步基础
2023-10-24 22:48:12
前言
说起 JUC(Java 并发包),我们常常会想起其中的线程池(ExecutorService)。然而,今天让我们将目光投向另一个核心模块 AQS(AbstractQueuedSynchronizer),它在 JUC 中扮演着同步器的基石角色。比如,我们熟知的 ReentrantLock 便正是基于 AQS 构建的。
AQS 的核心思想
AQS 的设计理念在于引入一个共享且可变的状态变量,称为 state。通过对 state 进行操作,我们可以实现线程之间的同步和协调。state 的取值可以代表不同的同步状态,如未加锁、独占加锁等。
独占模式
独占模式是 AQS 提供的三种基本模式之一,也是最常用的模式。在独占模式下,一次仅允许一个线程获得锁。当一个线程成功获取锁后,其他线程将被阻塞,直到该线程释放锁。
独占模式的实现
AQS 通过一个名为 CLH 队列(Craig, Landin, and Hagersten)来实现独占模式。CLH 队列是一个链表,其中每个节点代表一个请求获取锁的线程。当一个线程尝试获取锁时,它会首先尝试 CAS(Compare And Swap) state 的值。如果 state 为 0(表示未加锁),则该线程会将 state 设置为 1(表示已加锁),并将其自身添加到队列尾部。
如果 CAS 失败,说明另一个线程已经获取了锁,该线程会自旋(轮询)检查 state,直到 state 变为 0。当 state 变为 0 时,该线程会再次尝试 CAS,直到成功获取锁。
获取锁的步骤
以下是获取独占锁的步骤:
- 如果 state 为 0,则 CAS 将其设置为 1 并获取锁。
- 如果 state 不为 0,则创建一个节点并将其添加到 CLH 队列尾部。
- 自旋检查 state,直到其变为 0。
- 重复步骤 1,直到成功获取锁。
释放锁的步骤
释放独占锁的步骤相对简单:
- 如果 state 为 0,则表明锁未被持有,直接返回。
- 如果 state 为 1,则 CAS 将其设置为 0,释放锁。
- 如果 state 不为 0,则表明锁已被其他线程持有,抛出异常。
独占模式的应用
独占模式广泛应用于各种同步场景,比如:
- ReentranLock: 一个可重入锁,允许一个线程多次获取同一把锁。
- Semaphore: 一种用于限制访问共享资源的信号量。
- CountDownLatch: 一种用于等待多个线程完成特定任务的同步工具。
结论
AQS 的独占模式是 JUC 中同步器设计的基石。通过理解其工作原理,我们可以更好地理解 JUC 中各种同步类的行为。独占模式的广泛应用凸显了其在构建并发程序中的重要性。