【JAVA】让 ChatGPT 来浅说 AQS

前言

又迎来了一年一度的金三银四,虽然说今年的大环境不好,但是招聘还是在火热进行中。

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

那么接下来,让我们使用 ChatGPT 并结合自己的理解来讲述一下 AQS 的相关内容。

什么是 AQS

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

大致意思就是说,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 是如何实现地:

首先,AQS 的内部维护了一个 FIFO 的双向链表,用于存储等待获取锁的线程。当一个线程调用 acquire 方法时,如果当前没有其他线程持有锁,则直接获取锁;否则,将当前线程加入等待队列,并阻塞线程,直到获取到锁的时候再唤醒。

其次,AQS 还提供了一个 ConditionObject 类,用于实现线程的等待/通知机制。每个ConditionObject对象内部都维护了一个等待队列,用于存储等待条件满足的线程。当一个线程调用 await 方法时,将当前线程加入等待队列,并阻塞线程,直到条件满足的时候再唤醒;当一个线程调用 signal 方法时,将等待队列的第一个线程唤醒,使其从等待队列中移除,并加入到同步队列中等待获取锁。在使用 ConditionObject 时,需要先获取锁,才能调用 awaitsignal 方法。

最后,AQS还提供了一个 getStatesetState 方法,用于获取和设置当前同步状态。这个同步状态可以用于实现不同的同步语义,如读写锁中的读锁计数器。在实现自定义同步器时,可以使用这些方法来实现特定的同步语义。

需要注意的是,虽然 AQS 提供了强大的同步器框架,但是自定义同步器需要非常小心,以避免出现死锁、饥饿等问题。在实现自定义同步器时,需要仔细分析应用场景,理清线程之间的依赖关系,以确保同步器的正确性和高效性。

AQS 实现不可重入锁

上述光说理论可能不易于理解,因此让 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 方法来释放锁。

设计一个测试用例,发现正如我们所预料的那样,获取锁与释放锁的功能正常,且当对象有锁之后,不能再获取到该对象了,即不可重入:

上述示例代码只是 AQS 的一个非常简单的应用,更复杂的应用可以参考 Java 中 ReentrantLockCountDownLatchSemaphore 等同步器的实现。

#java##后端##java研发#
全部评论
看似火热,实则内卷
点赞 回复 分享
发布于 2023-03-28 17:27 重庆

相关推荐

从小父母离异家里没人管,靠着心里的不安和学校的环境也算是坚持到了学有所成的地步。到了大学环境开始松散不知道该做什么,只觉得在不挂科的基础上能往上考多少就考多少,等到秋招来临才发现自己有多么幼稚无能,今年九月份初才发现自己原来连一个求职的方向都没有。因为之前做过前后端一体的课设,算是有过了解,而对于其他岗位连做什么都不知道,因此这一个半个月在越来越焦虑的同时埋头苦学,事到如今想要活下去我似乎只能走前端这条路了,9月初先是靠着虚假夸大能力的简历得到一些笔试来确定了考察的方向,有一个大厂的无笔试面试最终是拒绝了没有勇气去面对。然后在这个基础上埋头苦学,如今也算是搭好了自己前端学习的框架和思考的瞄,可以逐渐给自己扩展新的知识和能力了,但这并不是一件多好的事儿,因为我发现学的越多越焦虑,学的越多便越无力。因为我感觉我如今努力学习的知识都是竞争对手们早就掌握了的东西,我如今困惑追求答案的难题早就被别人解决。别人早就能得心应手地做出项目而我连思考都会卡壳,看着别人的笔试和面经上那些闻所未闻的题目,我才知道别人到底有多强而我有多幼稚,我什么时候才能达到别人那种堪称熟练的能力呢?而且网上的焦虑越多越多,即便是真有这么高的能力最后也大概落得一个低薪打工人的下场,我真的感到迷茫。秋招都快结束了,而我还在继续痛苦的学习之旅,这些天找前端面试发现似乎问的有些简单跟网上搜到的内容不符(可能因为并不是大厂),我是不是本来就没打算被招所以别人懒得细问呢?我不知道,我只能继续总结下去学习下去,不管如何我都要活下去,如果我能早一些准备就好了,如果暑假能意识到现在这个情况就好了,可惜没有如果。种下一棵树的最好时间是十年前,其次是现在,虽然我相信自己的学习能力,但已经错过了最好的时机,只能在焦虑与痛苦中每天坚持学下去。目前的路还有很长很长,先去把typescript看了,再去巩固vue3的基础,再去练习elementui的使用,如果这能找到实习的话就好了。接下来呢?去学uniapp和小程序,不管如何我都要对得起曾经努力的自己。即便我们都感到痛苦,但我心中还是希望我们都能靠自己的努力来获取自己想要的幸福。
紧张的牛牛等一个of...:在担心什么呢,有一手985的学历在,就算是小厂别人都会要的,咱们双非的人更多,多少还在沉沦的,怕什么了
一句话证明你在找工作
点赞 评论 收藏
分享
评论
2
6
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务