什么是 AbstractQueuedSynchronizer


共计 2918 个字符,预计需要花费 8 分钟才能阅读完成。

AbstractQueuedSynchronizer

今天在b站看了关于 AbstractQueuedSynchronizer 的合集,稍微了解了一下 AbstractQueuedSynchronizer 的工作原理,简单记录一下。

AbstractQueuedSynchronizer (AQS) 是 Java 中 java.util.concurrent 包的一部分,是一个基础框架类,用于实现锁和同步器(如 ReentrantLock、Semaphore、CountDownLatch 等)。

AQS 是一个抽象类,主要为实现同步器提供了以下关键功能:

  1. 同步状态的管理:

    • AQS 使用一个 int 类型的变量(state)来表示同步状态。(该变量使用 volatile 修饰)
    • 提供了 getStatesetStatecompareAndSetState 方法,允许子类安全地修改同步状态。
  2. 线程队列:

    • AQS 使用一个先进先出的等待队列来管理被阻塞的线程。
    • 当线程尝试获取同步资源失败时,会被加入这个等待队列。(需要注意的是,不是获取锁失败马上就加入阻塞队列,而是中间有很多次获取锁的机会
  3. 独占模式和共享模式:

    • 独占模式(Exclusive Mode):一个线程独占资源,例如 ReentrantLock
    • 共享模式(Shared Mode):多个线程可以共享资源,例如 SemaphoreCountDownLatch
  4. 模板方法模式

    AQS 定义了一些模板方法(如 tryAcquiretryReleasetryAcquireSharedtryReleaseShared),需要子类去实现它们以完成具体的同步逻辑。

    • boolean tryAcquire(int arg): 尝试以独占模式获取资源,由子类实现。
    • boolean tryRelease(int arg): 尝试以独占模式释放资源,由子类实现。
    • int tryAcquireShared(int arg): 尝试以共享模式获取资源,由子类实现。
    • boolean tryReleaseShared(int arg): 尝试以共享模式释放资源,由子类实现。
    • boolean isHeldExclusively(): 判断当前同步器是否被某线程独占。

关于为什么这些方法并非都是抽象方法,而是普通方法,是希望子类按需实现,而不是都需要把这些方法实现。

ReentrantLock加锁流程

下图是 ReentrantLock 加锁的流程图:

什么是 AbstractQueuedSynchronizer

/>

LockSupport阻塞和唤醒

上图中线程阻塞和唤醒是通过 LockSupport 实现的。LockSupport.park()LockSupport.unpark(Thread thread) 是 Java 中提供的低级线程阻塞和唤醒机制。它们是 java.util.concurrent.locks.LockSupport 类的一部分,用于实现线程的挂起和唤醒操作。

  1. park():

    • 将当前线程阻塞(挂起),直到其他线程调用 unpark(Thread thread) 或线程被中断。
    • 是一个相对低级的阻塞操作,用于替代 Thread.suspend() 的不安全行为。
    • 它允许线程在等待某个条件满足时挂起,而不消耗 CPU 资源。
  2. unpark(Thread thread):

    • 唤醒指定线程,使其从 park() 状态恢复。
    • 如果被唤醒的线程当前未被阻塞在 park(),调用 unpark() 会记录一个许可,下次调用 park() 时立即返回。

假设有一下代码,想要按顺序打印 ABC,但是由于线程执行是不固定的,所以每次执行会出现不同的结果。

public class LockSupportTest {

    public static void main(String[] args) {
        LockSupportTest lockSupportTest = new LockSupportTest();
        Thread t1 = new Thread(() -> lockSupportTest.printA());
        Thread t2 = new Thread(() -> lockSupportTest.printB());
        Thread t3 = new Thread(() -> lockSupportTest.printC());
        t1.start();
        t2.start();
        t3.start();
    }

    private void printA(){
        try {
            Thread.sleep(5);
            System.out.print("A");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void printB(){
        try {
            Thread.sleep(5);
            System.out.print("B");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void printC(){
        try {
            Thread.sleep(5);
            System.out.print("C");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

此时使用 LockSupport 改造一下代码即可稳定顺序打印 ABC:

public class LockSupportTest {

    public static void main(String[] args) {
        LockSupportTest lockSupportTest = new LockSupportTest();
        Thread t3 = new Thread(() -> lockSupportTest.printC());
        Thread t2 = new Thread(() -> lockSupportTest.printB(t3));
        Thread t1 = new Thread(() -> lockSupportTest.printA(t2));

        t1.start();
        t2.start();
        t3.start();
    }

    private void printA(Thread thread){
        try {
            Thread.sleep(5);
            System.out.print("A");
            LockSupport.unpark(thread);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void printB(Thread thread){
        try {
            Thread.sleep(5);
            LockSupport.park();
            System.out.print("B");
            LockSupport.unpark(thread);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void printC(){
        try {
            Thread.sleep(5);
            LockSupport.park();
            System.out.print("C");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

Tips:清朝云网络工作室

阅读剩余
THE END