3.Java面试题之AQS

ops/2024/9/23 15:20:53/

1. 写在前面

在这里插入图片描述

AQS(AbstractQueuedSynchronizer)是Java并发包(java.util.concurrent)中的一个抽象类,用于实现同步器(如锁、信号量、栅栏等)。AQS提供了一种基于FIFO队列的机制来管理线程的竞争和等待状态。其主要作用是简化同步器的实现,通过提供通用的同步状态管理和线程排队机制,使得开发者可以专注于特定同步器的逻辑。了解AQS的工作原理和应用场景是高级Java开发者需要掌握的重要技能。以下是一些常见的AQS面试题及其详细解答。

2. AQS的工作原理是什么?

AQS的核心工作原理基于一个FIFO等待队列和一个同步状态(state)。其主要步骤如下:

  • 同步状态:AQS通过一个 int 类型的变量 state 来表示同步状态。子类通过重写 tryAcquire、tryRelease 等方法来定义获取和释放同步状态的逻辑。
  • 等待队列:当线程无法获取同步状态时,会被加入到AQS的FIFO等待队列中,队列中的每个节点(Node)表示一个等待的线程。
  • 独占模式和共享模式:AQS支持独占模式(如独占锁)和共享模式(如共享锁、信号量)。在独占模式下,只有一个线程可以获取同步状态;在共享模式下,多个线程可以同时获取同步状态。
  • 模板方法:AQS通过模板方法模式提供了通用的同步机制,子类只需实现特定的同步逻辑。

3. AQS中的Node节点是什么?其作用是什么?

AQS中的Node节点是一个内部类 AbstractQueuedSynchronizer.Node,用于表示等待队列中的每个线程。Node节点包含以下重要信息:

  • 线程引用:表示当前节点所关联的线程。
  • 等待状态:表示当前节点的等待状态,如 SIGNAL(等待唤醒)、CANCELLED(取消)等。
  • 前驱和后继节点:用于在等待队列中形成双向链表。
  • 模式:表示当前节点是独占模式还是共享模式。

Node节点的作用是管理等待队列中的线程状态和排队顺序,确保线程能够按照FIFO顺序被唤醒和执行。

4. AQS中的独占模式和共享模式有什么区别?

AQS支持两种模式:独占模式和共享模式。

4.1 独占模式

  • 只有一个线程可以获取同步状态
  • 典型应用:独占锁(如 ReentrantLock)
  • 主要方法:tryAcquire、tryRelease

4.2 共享模式

  • 多个线程可以同时获取同步状态
  • 典型应用:共享锁(如 ReadWriteLock 中的读锁)、信号量(如 Semaphore)
  • 主要方法:tryAcquireShared、tryReleaseShared

5. 如何使用AQS实现一个简单的独占锁?

可以通过继承AQS并重写其方法来实现一个简单的独占锁。以下是一个示例:

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

6. 如何使用AQS实现一个简单的共享锁?

可以通过继承AQS并重写其方法来实现一个简单的共享锁。以下是一个示例:

java">import java.util.concurrent.locks.AbstractQueuedSynchronizer;public class SimpleSharedLock {private static class Sync extends AbstractQueuedSynchronizer {@Overrideprotected int tryAcquireShared(int arg) {for (;;) {int current = getState();int newCount = current + arg;if (compareAndSetState(current, newCount)) {return newCount;}}}@Overrideprotected boolean tryReleaseShared(int arg) {for (;;) {int current = getState();int newCount = current - arg;if (compareAndSetState(current, newCount)) {return newCount == 0;}}}}private final Sync sync = new Sync();public void lock() {sync.acquireShared(1);}public void unlock() {sync.releaseShared(1);}
}

7. AQS的公平锁和非公平锁有什么区别?

AQS支持公平锁和非公平锁两种模式:

7.1 公平锁

  • 线程按照FIFO顺序获取锁,先到先得。
  • 公平锁避免了线程饥饿,但可能会导致较高的上下文切换开销。
  • 典型实现:ReentrantLock 的公平模式。

7.2 非公平锁

  • 线程可以插队获取锁,不保证FIFO顺序。
  • 非公平锁可能会导致线程饥饿,但通常性能较高,因为减少了上下文切换。
  • 典型实现:ReentrantLock 的非公平模式(默认)。

8. AQS的Condition机制是如何实现的?

AQS的Condition机制通过内部类 ConditionObject 实现。ConditionObject 提供了 await 和 signal 等方法,用于线程的等待和唤醒。其工作原理如下:

  • 等待队列:每个Condition对象都有一个单独的等待队列,线程调用 await 方法时,会被加入到该等待队列中,并释放当前持有的锁。
  • 唤醒机制:线程调用 signal 方法时,会从等待队列中唤醒一个线程,并将其移到同步队列中,等待获取锁。
java">import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;public class SimpleLockWithCondition {private static class Sync extends AbstractQueuedSynchronizer {@Overrideprotected boolean tryAcquire(int arg) {if (compareAndSetState(0, 1)) {setExclusiveOwnerThread(Thread.currentThread());return true;}return false;}@Overrideprotected boolean tryRelease(int arg) {if (getState() == 0) throw new IllegalMonitorStateException();setExclusiveOwnerThread(null);setState(0);return true;}protected boolean isHeldExclusively() {return getState() == 1;}Condition newCondition() {return new ConditionObject();}}private final Sync sync = new Sync();public void lock() {sync.acquire(1);}public void unlock() {sync.release(1);}public Condition newCondition() {return sync.newCondition();}
}

http://www.ppmy.cn/ops/87434.html

相关文章

Docker镜像仓库

目录 前言 1. 常见的镜像仓库 2. 搭建私有镜像仓库 3. 私有库的推送、拉取镜像 4. 总结 前言 Docker镜像仓库简单来说就是存储和管理Docker镜像的平台或服务。它允许开发人员上传自己创建的镜像,并与团队成员共享和协作使用。 1. 常见的镜像仓库 镜像仓库有公…

Java实战 - 实现八皇后的回溯算法

作者:逍遥Sean 简介:一个主修Java的Web网站\游戏服务器后端开发者 主页:https://blog.csdn.net/Ureliable 觉得博主文章不错的话,可以三连支持一下~ 如有疑问和建议,请私信或评论留言! 前言 当涉及经典的回…

ChatGPT:Java 中的 MultipartFile 是什么?

ChatGPT:Java 中的 MultipartFile 是什么? MultipartFile 是 Spring 框架中用于处理多部分文件上传的接口。它提供了一种方便的方式来处理 HTTP 请求中的文件数据。通常在 web 应用程序中,当用户上传文件时,服务器会收到包含文件数…

高保真虚拟人视频生成解决方案:MuseV MuseTalk

一、引言 随着人工智能技术的发展,尤其是深度学习的进步,虚拟人的生成和交互变得越来越真实和自然。虚拟人不仅能够应用于娱乐、教育、商业等多个领域,还能带来前所未有的沉浸式体验。本文介绍了一种高保真虚拟人视频生成解决方案,该方案结合了MuseV和即将推出的MuseTalk,…

android前台服务

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。 未经允许不得转载 目录 一、导读二、使用2.1 添加权限2.2 新建…

理解 Objective-C 中 `+load` 方法的执行顺序

理解 Objective-C 中 load 方法的执行顺序 在 Objective-C 中,load 方法是在类或分类被加载到内存时调用的。它在程序启动过程中非常早的阶段执行,用于在类或分类被加载时进行一些初始化工作。理解 load 方法的执行顺序对于编写可靠的 Objective-C 代码…

IDEA切换分支,会影响当前在跑的项目吗?

说明:本文测试,在IDEA中运行项目,然后切换分支,是否会影响当前正在跑的项目 准备工作 首先,创建一个Git项目,接口如下: import org.springframework.web.bind.annotation.GetMapping; import…

WinFrom调用webapi接口方法及其应用实例

1.WinFrom调用webapi接口方法 最近项目要在winfrom项目中调用webAPI&#xff0c;故在网上查找资料&#xff0c;找到了一个WinFrom调用webapi接口的通用方法&#xff0c;关键代码&#xff1a; #region WinFrom调用webapi接口通用方法private async Task<string> InvokeWe…