0%

AQS(AbstractQueuedSynchronizer):Java 同步框架的基石

AQS(AbstractQueuedSynchronizer,队列同步器)是 Java 并发包(JUC)的核心基础组件,为 ReentrantLock、Semaphore、CountDownLatch 等同步工具提供统一的底层支持。它通过同步状态管理FIFO 等待队列实现了高效的线程同步机制。本文将深入解析 AQS 的设计原理、核心组件及工作流程。

AQS 的核心设计思想

AQS 的核心目标是简化同步组件的实现,其设计基于模板方法模式

  • 子类通过继承 AQS 并实现特定抽象方法(如tryAcquiretryRelease)来定义同步状态的获取与释放规则;
  • AQS 自身实现了队列管理、线程阻塞 / 唤醒等通用逻辑,无需子类重复开发。

核心组件

  • 同步状态(state):一个volatile int变量,用于表示资源的占有状态(如 0 表示未占用,1 表示已占用);
  • FIFO 等待队列:双向链表结构,存储等待获取资源的线程节点;
  • 条件队列(Condition):与Lock配合使用,实现线程间的条件等待。

AQS 核心源码解析

AQS的子类使用

同步状态(state)

阅读全文 »

线程通信:从 synchronized 到 Lock 的演进与实践

线程通信是多线程编程中的核心问题,用于协调多个线程间的执行顺序和资源共享。Java 提供了两种主要的线程通信机制:基于synchronizedwait/notify和基于LockCondition接口。本文将深入对比这两种机制,并解析其适用场景。

synchronized 与 Object 类的线程通信方法

核心方法

Java 的每个对象都继承自Object类,提供了三个关键方法用于线程间通信:

  • wait():使当前线程释放对象锁并进入等待状态,直到被其他线程唤醒;
  • notify():随机唤醒一个在该对象上等待的线程;
  • notifyAll():唤醒所有在该对象上等待的线程。

使用规则

这些方法必须在synchronized修饰的代码块或方法中调用,否则会抛出IllegalMonitorStateException

示例:交替打印 1-100

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class AlternatePrint {  
private static final Object LOCK = new Object();
private static int count = 1;

public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
synchronized (LOCK) {
// 检查条件,避免虚假唤醒
while (count > 100) break;
System.out.println(Thread.currentThread().getName() + ": " + count++);
LOCK.notify(); // 唤醒其他线程
try {
if (count <= 100) LOCK.wait(); // 释放锁并等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}, "线程1");

Thread t2 = new Thread(() -> {
while (true) {
synchronized (LOCK) {
while (count > 100) break;
System.out.println(Thread.currentThread().getName() + ": " + count++);
LOCK.notify();
try {
if (count <= 100) LOCK.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}, "线程2");

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

虚假唤醒(Spurious Wakeup)

Java 规范允许wait()在没有被显式唤醒的情况下返回,称为 “虚假唤醒”。因此,正确的使用方式是将wait()放在while循环中,确保条件满足:

阅读全文 »

synchronized关键字:Java 中的同步机制与锁升级原理

synchronized是 Java 中用于保证线程安全的核心关键字,通过实现同步互斥来避免多线程并发访问共享资源时的竞态条件。随着 JVM 的演进,synchronized从早期的 “重量级锁” 发展为具备锁升级能力的高效同步机制。本文将深入解析其作用、实现原理及锁升级过程。

synchronized 的核心作用

synchronized的核心目标是解决多线程并发问题,具体通过以下特性实现:

  1. 原子性:保证临界区代码(被synchronized修饰的代码块或方法)同一时刻只能被一个线程执行,确保操作不可分割;
  2. 可见性:通过 JMM(Java 内存模型)的规则,确保一个线程对共享变量的修改能被其他线程立即看到;
  3. 有序性:禁止指令重排序,保证临界区代码按顺序执行。

synchronized 的使用方式

synchronized可修饰代码块或方法,其锁对象的选择决定了同步粒度:

修饰实例方法

锁对象为当前实例(this),同一实例的多个synchronized方法共享同一把锁。

1
2
3
public synchronized void method() {  
// 临界区代码
}

修饰静态方法

锁对象为当前类的 Class 对象(如MyClass.class),所有静态synchronized方法共享同一把锁。

阅读全文 »

volatile关键字:Java 内存可见性的实现与应用

在多线程编程中,volatile是一个重要的关键字,用于保证共享变量的可见性禁止指令重排序。本文将深入解析volatile的作用、原理及最佳实践。

可见性问题的根源

CPU 缓存架构与内存可见性

现代 CPU 为提高数据访问速度,引入多级缓存(L1、L2、L3)。当线程访问共享变量时:

  • 变量值会从主内存拷贝到 CPU 缓存;
  • 线程修改变量后,新值可能暂存于缓存,未及时刷新到主内存;
  • 其他线程的 CPU 缓存中可能仍保留旧值,导致可见性问题

指令重排序与执行乱序

编译器和 CPU 为优化性能,可能对指令进行重排序,只要不影响单线程执行结果。但在多线程环境中,重排序可能导致逻辑错误,例如:

1
2
3
4
5
6
7
// 线程A  
instance = new Singleton(); // 可能被重排序为:分配内存 → 设置引用 → 初始化对象

// 线程B
if (instance != null) { // 此时instance已指向内存,但对象可能未初始化
instance.use(); // 可能导致NPE
}

volatile 的核心作用

1. 保证可见性

volatile修饰的变量具有以下特性:

阅读全文 »

RestTemplate 401 异常解析失败问题解决方案

当使用 RestTemplate 默认构造器时,处理 401 等错误响应时常常会遇到无法正确获取响应体的问题。这是因为默认的请求工厂实现存在一定的局限性。

问题原因分析

RestTemplate 默认使用SimpleClientHttpRequestFactory,它基于 JDK 的HttpURLConnection实现。当遇到 401 等错误状态码时:

1
2
3
if (response.getStatusCode().isError()) { // 4XX或者5XX
InputStream inputStream = response.getBody();
}

SimpleClientHttpResponsegetBody()方法实现存在问题:

1
2
3
4
5
6
7
@Override
public InputStream getBody() throws IOException {
// 当响应码为401时,getErrorStream()可能返回null
InputStream errorStream = this.connection.getErrorStream();
this.responseStream = (errorStream != null ? errorStream : this.connection.getInputStream());
return this.responseStream;
}

此时调用getInputStream()会抛出IOException,导致无法获取错误响应体。这是因为 JDK 的HttpURLConnection在处理 401 等认证错误时,不会将响应体通过getErrorStream()返回,而是直接抛出异常。

解决方案

解决这个问题的方法是更换 RestTemplate 的请求工厂,使用 Apache 的 HttpClient 实现:

1
2
// 添加Apache HttpClient依赖后
RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());

使用HttpComponentsClientHttpRequestFactory的优势:

  1. 能正确处理 401 等错误响应的响应体
  2. 提供更丰富的 HTTP 特性支持
  3. 更好的连接池管理
  4. 更灵活的超时设置

完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

@Bean
public RestTemplate restTemplate() {
// 使用HttpComponentsClientHttpRequestFactory替代默认工厂
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();

// 可以在这里配置超时时间
requestFactory.setConnectTimeout(5000);
requestFactory.setReadTimeout(5000);

return new RestTemplate(requestFactory);
}

// 使用示例
public void useRestTemplate() {
RestTemplate restTemplate = restTemplate();
try {
// 执行请求
org.springframework.http.ResponseEntity<String> response =
restTemplate.getForEntity("https://api.example.com/protected-resource", String.class);

if (response.getStatusCode().is2xxSuccessful()) {
// 处理成功响应
String body = response.getBody();
} else {
// 处理错误响应,此时可以正常获取响应体
String errorBody = response.getBody();
}
} catch (org.springframework.web.client.HttpStatusCodeException e) {
// 捕获异常并获取错误信息
String errorBody = e.getResponseBodyAsString();
int statusCode = e.getRawStatusCode();
// 处理异常
}
}
}

依赖配置

使用HttpComponentsClientHttpRequestFactory需要添加 Apache HttpClient 依赖:

1
2
3
4
5
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3</version>
</dependency>

Gradle:

1
implementation 'org.apache.httpcomponents.client5:httpclient5:5.3'

总结

通过替换 RestTemplate 的请求工厂为HttpComponentsClientHttpRequestFactory,可以解决 401 等错误响应解析失败的问题,同时获得更强大的 HTTP 客户端功能。这是在生产环境中使用 RestTemplate 的推荐配置方式