本文正在参与「金石方案」
前语
又迎来了一年一度的金三银四,尽管说今年的大环境欠好,可是招聘仍是在炽热进行中。
面试过 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,以下几个方面是必要的:
- 同步器:AQS 是同步器的一个笼统基类,通过承继 AQS 能够构建各种同步组件,如锁、信号量等。
- 状况:AQS 内部保护了一个状况变量,表明同步器的状况。同步器的详细含义由子类来界说。
- 行列:AQS 内部运用 FIFO 行列来存储等候获取同步状况的线程。当多个线程一起请求同步状况时,AQS 会将其中一个线程设置为独占形式,即该线程成为获取到同步状况的唯一持有者,其他线程则会被参加到等候行列中。
- 模板办法:AQS 采用了模板办法规划形式,在 AQS 中界说了一系列笼统办法和钩子办法,子类需求完成这些办法来界说自己的同步逻辑。
- CAS 和 volatile:AQS 内部运用了 CAS 和 volatile 等原语来保证同步器的正确性和并发性能。
总归,AQS 是 Java 中并发包中完成锁和其他同步器的根底结构,运用模板办法规划形式和 CAS 操作完成了高效、可扩展性高的同步器。了解 AQS 对于了解 Java 中并发编程的原理和完成十分重要。
AQS 怎么完成
那接下来问一下 ChatGPT AQS 是怎么完成地:
首要,AQS 的内部保护了一个 FIFO 的双向链表,用于存储等候获取锁的线程。当一个线程调用 acquire
办法时,假如当时没有其他线程持有锁,则直接获取锁;不然,将当时线程参加等候行列,并阻塞线程,直到获取到锁的时分再唤醒。
其次,AQS 还供给了一个 ConditionObject
类,用于完成线程的等候/通知机制。每个ConditionObject
目标内部都保护了一个等候行列,用于存储等候条件满意的线程。当一个线程调用 await
办法时,将当时线程参加等候行列,并阻塞线程,直到条件满意的时分再唤醒;当一个线程调用 signal
办法时,将等候行列的第一个线程唤醒,使其从等候行列中移除,并参加到同步行列中等候获取锁。在运用 ConditionObject
时,需求先获取锁,才干调用 await
、signal
办法。
最终,AQS还供给了一个 getState
和 setState
办法,用于获取和设置当时同步状况。这个同步状况能够用于完成不同的同步语义,如读写锁中的读锁计数器。在完成自界说同步器时,能够运用这些办法来完成特定的同步语义。
需求注意的是,尽管 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 中 ReentrantLock
、CountDownLatch
、Semaphore
等同步器的完成。
后记
以上便是让 ChatGPT 来浅说 AQS的一切内容了,希望本篇博文对我们有所帮助!
上篇精讲:【JAVA】怎么监控和确诊JVM堆内和堆外内存运用?
我是,期待你的关注;
创作不易,请多多支撑;
系列专栏:面试精讲 JAVA