本文正在参与「金石方案」

前语

又迎来了一年一度的金三银四,尽管说今年的大环境欠好,可是招聘仍是在炽热进行中。

面试过 Java 工程师的小伙伴都知道,Java 中的 AQS 是面试高频题,面试官上来就直接了当地问,AQS 知道是什么吧,来讲讲它是怎样完成的,以及哪些地方用到了它。

那么接下来,让咱们运用 ChatGPT 并结合自己的了解来叙述一下 AQS 的相关内容。

什么是 AQS

当博主发问 ChatGPT 什么是 AQS 时,ChatGPT 给出了如下回答:

【JAVA】让 ChatGPT 来浅说 AQS

大致意思便是说,AQS 全称 AbstractQueuedSynchronizer,是 Java 中并发包中用于完成锁和其他同步器的根底结构。它运用了 CAS 操作和 unsafe 类来完成同步器的状况更新以及线程的挂起和唤醒等操作,供给了一种通用的、高效且可扩展的同步机制,能够用来构建各种同步组件。

AQS 内部保护了一个 FIFO 行列,用于存储等候获取同步状况的线程。一起界说了一些模板办法,这些办法被子类完成,用于完成不同类型的同步器,例如 ReentrantLock、CountDownLatch、Semaphore 等。

AQS 运用了一种独特的模板办法规划形式,运用内部状况(一个 volatile 润饰的 state 变量)来操控同步器的行为,子类通过完成模板办法来操控同步器的状况改变。AQS 的内部状况能够被子类用于完成独占式、共享式的同步器。

综上,想要了解 AQS,以下几个方面是必要的:

  1. 同步器:AQS 是同步器的一个笼统基类,通过承继 AQS 能够构建各种同步组件,如锁、信号量等。
  2. 状况:AQS 内部保护了一个状况变量,表明同步器的状况。同步器的详细含义由子类来界说。
  3. 行列:AQS 内部运用 FIFO 行列来存储等候获取同步状况的线程。当多个线程一起请求同步状况时,AQS 会将其中一个线程设置为独占形式,即该线程成为获取到同步状况的唯一持有者,其他线程则会被参加到等候行列中。
  4. 模板办法:AQS 采用了模板办法规划形式,在 AQS 中界说了一系列笼统办法和钩子办法,子类需求完成这些办法来界说自己的同步逻辑。
  5. CAS 和 volatile:AQS 内部运用了 CAS 和 volatile 等原语来保证同步器的正确性和并发性能。

总归,AQS 是 Java 中并发包中完成锁和其他同步器的根底结构,运用模板办法规划形式和 CAS 操作完成了高效、可扩展性高的同步器。了解 AQS 对于了解 Java 中并发编程的原理和完成十分重要。

AQS 怎么完成

那接下来问一下 ChatGPT AQS 是怎么完成地:

【JAVA】让 ChatGPT 来浅说 AQS

首要,AQS 的内部保护了一个 FIFO 的双向链表,用于存储等候获取锁的线程。当一个线程调用 acquire 办法时,假如当时没有其他线程持有锁,则直接获取锁;不然,将当时线程参加等候行列,并阻塞线程,直到获取到锁的时分再唤醒。

其次,AQS 还供给了一个 ConditionObject 类,用于完成线程的等候/通知机制。每个ConditionObject目标内部都保护了一个等候行列,用于存储等候条件满意的线程。当一个线程调用 await 办法时,将当时线程参加等候行列,并阻塞线程,直到条件满意的时分再唤醒;当一个线程调用 signal 办法时,将等候行列的第一个线程唤醒,使其从等候行列中移除,并参加到同步行列中等候获取锁。在运用 ConditionObject 时,需求先获取锁,才干调用 awaitsignal 办法。

最终,AQS还供给了一个 getStatesetState 办法,用于获取和设置当时同步状况。这个同步状况能够用于完成不同的同步语义,如读写锁中的读锁计数器。在完成自界说同步器时,能够运用这些办法来完成特定的同步语义。

需求注意的是,尽管 AQS 供给了强壮的同步器结构,可是自界说同步器需求十分小心,以避免出现死锁、饥饿等问题。在完成自界说同步器时,需求仔细分析使用场景,理清线程之间的依靠关系,以确保同步器的正确性和高效性。

AQS 完成不行重入锁

上述光说理论或许不易于了解,因此让 ChatGPT 生成一个与 AQS 相关的代码示例:

【JAVA】让 ChatGPT 来浅说 AQS

相关代码如下:

import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class SimpleLock {
    private final Sync sync = new Sync();
    private static class Sync extends AbstractQueuedSynchronizer {
        @Override
        protected boolean tryAcquire(int arg) {
            if (getState() == 0 && compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        @Override
        protected boolean tryRelease(int arg) {
            if (getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }
    }
    public void lock() {
        sync.acquire(1);
    }
    public void unlock() {
        sync.release(1);
    }
}

在上述示例代码中,咱们首要界说了一个承继自 AbstractQueuedSynchronizer 的内部类 Sync,用于完成不行重入锁。

然后,咱们在 tryAcquire 办法中尝试获取锁,假如当时状况为0,且能够运用 CAS 操作将状况修改为1,表明成功获取到锁,不然获取锁失利。

tryRelease 办法中,咱们开释锁,首要查看当时状况是否为0,假如是0,表明当时没有线程持有锁,抛出不合法监视器状况异常,不然,运用 CAS 操作将状况修改为0,并将持有锁的线程设置为 null。

isHeldExclusively 办法中,咱们判别当时是否有线程持有锁,假如状况为1,表明有线程持有锁,回来 true,不然回来 false。

然后,咱们界说一个 SimpleLock 类,运用 Sync 内部类完成不行重入锁。在 lock 办法中,咱们调用 acquire 办法来获取锁;在 unlock 办法中,咱们调用 release 办法来开释锁。

规划一个测试用例,发现正如咱们所预料的那样,获取锁与开释锁的功能正常,且当目标有锁之后,不能再获取到该目标了,即不行重入:

【JAVA】让 ChatGPT 来浅说 AQS

上述示例代码仅仅 AQS 的一个十分简略的使用,更复杂的使用能够参考 Java 中 ReentrantLockCountDownLatchSemaphore 等同步器的完成。

后记

以上便是让 ChatGPT 来浅说 AQS的一切内容了,希望本篇博文对我们有所帮助!

上篇精讲:【JAVA】怎么监控和确诊JVM堆内和堆外内存运用?

我是,期待你的关注;

创作不易,请多多支撑;

系列专栏:面试精讲 JAVA