ThreadLocal
和 Thread
是 Java 中两个与线程相关的概念,它们在多线程编程中有不同的作用和使用场景。
Thread
是 Java 中表示一个线程的类。每个 Thread
对象表示一个单独的执行路径,即程序中的一个执行流。Java 中的多线程是通过创建 Thread
对象来实现的。每个 Thread
可以拥有自己的执行逻辑,通常是通过继承 Thread
类或实现 Runnable
接口来定义线程执行的任务。
特点:
Thread
对象代表一个独立的执行线程。示例:
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running...");
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start(); // 启动线程
}
}
ThreadLocal
是 Java 提供的一种用于实现线程局部存储(Thread-Local Storage, TLS)的类。每个 ThreadLocal
变量在每个线程中都有一个独立的副本,每个线程访问自己副本的数据,不会与其他线程发生冲突。
特点:
ThreadLocal
允许每个线程都拥有一个独立的变量副本,因此每个线程都可以独立地存储和读取该变量的值。ThreadLocal
变量的生命周期由线程的生命周期决定,在线程结束时,这些局部变量会自动回收。使用场景:
synchronized
来同步数据,减少了资源争用,提高了性能。示例:
public class ThreadLocalExample {
// 创建一个 ThreadLocal 变量
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);
public static void main(String[] args) {
Runnable task = () -> {
// 每个线程都有自己独立的副本
System.out.println(Thread.currentThread().getName() + " ThreadLocal value: " + threadLocal.get());
threadLocal.set(threadLocal.get() + 1); // 修改当前线程的值
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
}
}
在这个例子中,每个线程都会有自己的 threadLocal
变量副本,修改一个线程的值不会影响另一个线程。
特性 | Thread |
ThreadLocal |
---|---|---|
用途 | 用于创建和管理独立的线程 | 用于为每个线程提供独立的变量副本 |
作用范围 | 代表一个独立的线程,用于执行任务 | 允许每个线程持有自己独立的变量副本 |
线程间共享数据 | 各线程之间共享数据 | 各线程的数据是隔离的,不会相互影响 |
存储位置 | 存储在线程的堆栈中,线程之间共享全局资源 | 存储在线程局部的内存空间,每个线程有自己的副本 |
使用场景 | 需要并发执行的任务 | 需要为每个线程提供独立数据的场景 |
Thread
:当需要并行处理多个任务时,例如启动多个线程来处理不同的工作负载。ThreadLocal
:当每个线程都需要保持一些独立的数据(如数据库连接、用户会话等),不希望线程之间相互干扰时,可以使用 ThreadLocal
。看看以下这段关键代码:
Thread thread = new Thread();
thread.start(); // 启动线程,会在新的线程中调用 run 方法,实现异步执行
thread.run(); // 当前线程直接执行 run 方法,不会启动新线程
run()
方法,但该调用是在新的线程中执行的,实现实际异步执行。start()
是实现 异步/多线程 的入口。run()
方法会在 当前线程(通常是主线程) 中执行,不具备并发性。public class MyThread extends Thread {
public void run() {
System.out.println("Thread name: " + Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.run(); // 当前线程执行 run 方法,输出线程名为 main
thread.start(); // 启动新线程,输出线程名为 Thread-0(或其他)
}
}
Thread name: main
Thread name: Thread-0
解释:
run()
是在主线程中,所以输出是 “main”。start()
创建了新线程,输出为 JVM 分配的线程名,如 “Thread-0”。方法 | 是否新线程 | 是否并发执行 |
---|---|---|
start() |
☑ 是 | ☑ 是 |
run() |
❌ 否 | ❌ 否 |
start()
来启动线程,不要直接调用 run()
。run()
只是在当前线程中执行一段方法,绝非实际的多线程。适用于需要自定义线程类,且线程行为较简单的情况。
class MyThread extends Thread {
public void run() {
// 线程要执行的任务
System.out.println("线程执行: " + Thread.currentThread().getName());
}
}
public class Demo {
public static void main(String[] args) {
new MyThread().start(); // 启动线程
}
}
推荐用于资源共享(多个线程访问同一实例),避免多继承问题。
class MyRunnable implements Runnable {
public void run() {
// 线程任务
System.out.println("线程执行: " + Thread.currentThread().getName());
}
}
public class Demo {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start(); // 启动线程
}
}
适用于需要线程返回结果、抛出异常的场景。
import java.util.concurrent.*;
class MyCallable implements Callable<String> {
public String call() {
// 返回值任务
return "线程返回值: " + Thread.currentThread().getName();
}
}
public class Demo {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new MyCallable()); // 提交任务
System.out.println(future.get()); // 获取返回结果
executor.shutdown(); // 关闭线程池
}
}
适用于高频率创建线程的场景,避免频繁创建/销毁线程带来的开销。
ExecutorService executor = Executors.newFixedThreadPool(2); // 创建固定线程池
executor.execute(() -> System.out.println("线程池执行: " + Thread.currentThread().getName()));
executor.shutdown(); // 关闭线程池
适用于简单的同步需求。
public synchronized void safeMethod() {
// 加锁区域,保证线程安全
System.out.println("线程安全: " + Thread.currentThread().getName());
}
适用于需要尝试加锁、定时锁、可中断锁等高级场景。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private final Lock lock = new ReentrantLock();
public void safeMethod() {
lock.lock(); // 显式加锁
try {
System.out.println("线程安全执行: " + Thread.currentThread().getName());
} finally {
lock.unlock(); // 释放锁
}
}
}
用于描述两个线程互相等待对方释放资源,容易导致死锁。
public class DeadlockDemo {
private static final Object resourceA = new Object();
private static final Object resourceB = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resourceA) {
System.out.println("线程1获取了资源A");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (resourceB) {
System.out.println("线程1获取了资源B");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resourceB) {
System.out.println("线程2获取了资源B");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (resourceA) {
System.out.println("线程2获取了资源A");
}
}
});
thread1.start();
thread2.start();
}
}
此例演示了一个双向锁死锁问题:
适用于读多写少的场景,提高并发性能。
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteExample {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
lock.readLock().lock(); // 获取读锁
try {
System.out.println("读取操作: " + Thread.currentThread().getName());
} finally {
lock.readLock().unlock(); // 释放读锁
}
}
public void write() {
lock.writeLock().lock(); // 获取写锁
try {
System.out.println("写入操作: " + Thread.currentThread().getName());
} finally {
lock.writeLock().unlock(); // 释放写锁
}
}
}
适用于单例模式等延迟初始化场景,保证线程安全的同时减少同步开销。
public class DoubleCheckedLockingSingleton {
// 使用 volatile 关键字确保可见性和禁止指令重排
private static volatile DoubleCheckedLockingSingleton instance;
// 私有构造函数,防止外部实例化
private DoubleCheckedLockingSingleton() {}
// 双重检查锁定获取单例实例的方法
public static DoubleCheckedLockingSingleton getInstance() {
// 第一次检查,未加锁
if (instance == null) {
// 同步块,加锁
synchronized (DoubleCheckedLockingSingleton.class) {
// 第二次检查,加锁后
if (instance == null) {
instance = new DoubleCheckedLockingSingleton();
}
}
}
return instance;
}
public static void main(String[] args) {
// 测试获取单例实例
DoubleCheckedLockingSingleton singleton1 = DoubleCheckedLockingSingleton.getInstance();
DoubleCheckedLockingSingleton singleton2 = DoubleCheckedLockingSingleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
双重检查锁定使用 volatile
确保指令不会被重排序,synchronized
保证线程安全。
Java 中线程的中断是一种协作机制,主要用于通知线程“你应该停止了”,而非强制终止线程。
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 执行任务
}
System.out.println("线程被中断,退出循环");
});
thread.start();
// 中断线程
thread.interrupt();
isInterrupted()
:判断线程是否被中断,不清除中断标志。Thread.interrupted()
:判断并清除当前线程的中断状态。Thread thread = new Thread(() -> {
while (true) {
if (Thread.interrupted()) {
System.out.println("检测到中断状态,退出线程");
break;
}
}
});
thread.start();
thread.interrupt();
Thread thread = new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("线程在 sleep 时被中断");
}
});
thread.start();
thread.interrupt();
stop()
等方法强制终止线程。isInterrupted()
或 Thread.interrupted()
,优雅退出。sleep()
、wait()
等被中断会抛出 InterruptedException
,可用 try-catch 处理。线程池用于重用线程资源,避免频繁创建和销毁线程造成资源浪费。 Java 提供的核心线程池类是 java.util.concurrent.ThreadPoolExecutor
。
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // corePoolSize 核心线程数
4, // maximumPoolSize 最大线程数
60, // keepAliveTime 非核心线程的空闲存活时间
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100), // 工作队列
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
线程池构造函数的七大参数如下:
常用拒绝策略有:
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(() -> {
// 执行任务逻辑
System.out.println("线程池任务: " + Thread.currentThread().getName());
});
executor.shutdown(); // 关闭线程池,等待任务执行完毕
Spring 中使用 @Scheduled
注解可轻松实现定时任务,底层依赖线程池调度。
@Component
public class MyScheduler {
@Scheduled(fixedRate = 5000) // 每隔 5 秒执行一次
public void task() {
System.out.println("定时任务执行: " + Thread.currentThread().getName());
}
}
SpringBoot 自动配置类 TaskSchedulingAutoConfiguration
会创建一个 ThreadPoolTaskScheduler
,其本质是基于 ScheduledThreadPoolExecutor
实现定时调度的线程池。
可以通过配置修改线程池参数:
spring:
task:
scheduling:
pool:
size: 10
@Scheduled
注解方法。ScheduledMethodRunnable
。ScheduledThreadPoolExecutor
按固定频率执行。Java 提供了几种常用线程池工厂方法,适用于不同的场景:
线程池类型 | 创建方式 | 使用场景 | 优点 | 缺点 |
---|---|---|---|---|
FixedThreadPool | Executors.newFixedThreadPool(n) | 固定数量线程,适用于任务量稳定的场景 | 限制线程数量,节省资源 | 无法动态扩容,任务过多可能阻塞 |
CachedThreadPool | Executors.newCachedThreadPool() | 高频短任务,适合处理大量瞬时请求 | 按需扩展线程数,响应快 | 线程数不受控,可能耗尽资源 |
SingleThreadExecutor | Executors.newSingleThreadExecutor() | 单线程串行执行任务,适用于日志记录等 | 顺序执行,线程安全 | 性能低,单点故障风险高 |
ScheduledThreadPool | Executors.newScheduledThreadPool(n) | 定时任务、周期任务调度 | 支持延迟和周期性执行 | 线程数固定,调度复杂 |
WorkStealingPool | Executors.newWorkStealingPool() | 多核 CPU 环境下高吞吐计算密集型任务 | 自动负载均衡,效率高 | JDK 8+ 支持,控制较少 |
为了提升线程池的性能和稳定性,以下是一些实践建议:
LinkedBlockingQueue
适合任务量较大、执行速度快的场景,SynchronousQueue
适合任务快速切换的场景。ThreadFactory
指定线程名称格式,便于日志跟踪和调试。shutdown()
或 shutdownNow()
,或使用 try-with-resources
结合封装类。通过使用线程池,Spring 能确保多个定时任务并发、安全、高效地运行。
通过上述各种方式和同步机制,开发者可以根据不同场景构建出高效、安全的并发程序。
new Thread()
有什么问题?当你每次都用 new Thread()
来创建线程时,可能会遇到这些问题:
new Thread()
启动后执行一次就终止了,不能重复使用,每次都要重新 new。new Thread()
没有控制机制,一不小心就开太多。特性 | new Thread() |
线程池(ExecutorService ) |
---|---|---|
线程复用 | ❌ 每次新建 | ✅ 可复用 |
性能 | ❌ 慢,系统开销大 | ✅ 更高效 |
管理线程数量 | ❌ 需要手动控制 | ✅ 有参数配置自动管理 |
是否适合高并发 | ❌ 线程过多易崩溃 | ✅ 有限资源管理,安全稳定 |
队列、拒绝策略等 | ❌ 不支持 | ✅ 支持完整控制 |
监控与扩展能力 | ❌ 差 | ✅ 强 |
✅ 正式项目中统一使用线程池,比如:
ThreadPoolExecutor
@Async
+ 自定义线程池CompletableFuture
本节将带你从手动构建线程池,到 Spring 异步注解 @Async
的使用,再深入到 CompletableFuture
实现异步任务并收集结果的完整方案。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.*;
@Configuration
public class ThreadPoolConfig {
@Bean("customExecutor")
public ExecutorService customExecutor() {
return new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60, // 空闲线程最大存活时间
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100), // 阻塞队列
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略:直接抛异常
);
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.ExecutorService;
@RestController
public class ThreadPoolController {
@Autowired
private ExecutorService customExecutor;
@PostMapping("/thread")
public void executeTasks() {
for (int i = 0; i < 20; i++) {
int taskId = i;
customExecutor.submit(() -> {
System.out.println("线程 " + Thread.currentThread().getName() + " 正在执行任务 " + taskId);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("线程 " + Thread.currentThread().getName() + " 完成任务 " + taskId);
});
}
}
}
策略名 | 描述 |
---|---|
AbortPolicy |
默认策略,直接抛出异常 |
CallerRunsPolicy |
任务由提交线程自己执行,降低任务提交速度 |
DiscardPolicy |
直接丢弃任务,不抛异常 |
DiscardOldestPolicy |
丢弃队列头部(最旧)任务,尝试执行当前任务 |
@Async
实现异步任务在启动类或配置类上添加注解:
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableAsync
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
public class AsyncConfig {
@Bean("asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("Async-Thread-");
executor.initialize();
return executor;
}
}
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncService {
@Async("asyncExecutor")
public void doTask(int taskId) {
System.out.println("【异步】线程 " + Thread.currentThread().getName() + " 开始任务 " + taskId);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("【异步】线程 " + Thread.currentThread().getName() + " 完成任务 " + taskId);
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@PostMapping("/async")
public void triggerAsyncTasks() {
for (int i = 0; i < 10; i++) {
asyncService.doTask(i);
}
System.out.println("主线程完成调用!");
}
}
@Async
+ CompletableFuture
获取返回结果适用于:
多个异步任务并发执行
收集所有任务结果后统一处理或返回
CompletableFuture
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class AsyncService {
@Async("asyncExecutor")
public CompletableFuture<String> doTaskWithResult(int taskId) {
System.out.println("【异步】线程 " + Thread.currentThread().getName() + " 开始任务 " + taskId);
try {
Thread.sleep(1000 + taskId * 100); // 模拟任务耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
String result = "任务 " + taskId + " 完成 by " + Thread.currentThread().getName();
System.out.println(result);
return CompletableFuture.completedFuture(result);
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@RestController
public class AsyncResultController {
@Autowired
private AsyncService asyncService;
@GetMapping("/async-results")
public List<String> getAsyncResults() throws Exception {
List<CompletableFuture<String>> futures = new ArrayList<>();
for (int i = 0; i < 5; i++) {
futures.add(asyncService.doTaskWithResult(i));
}
// 等待所有任务完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
// 收集结果
List<String> results = new ArrayList<>();
for (CompletableFuture<String> future : futures) {
results.add(future.get());
}
return results;
}
}
/async-results
):[
"任务 0 完成 by Async-Thread-1",
"任务 1 完成 by Async-Thread-2",
"任务 2 完成 by Async-Thread-3",
"任务 3 完成 by Async-Thread-4",
"任务 4 完成 by Async-Thread-5"
]
实现方式 | 适用场景 | 优势描述 |
---|---|---|
ExecutorService |
精细控制线程,适合通用应用 | 灵活性强,可完全自定义线程池细节 |
@Async |
Spring 项目异步执行任务 | 简洁优雅,易管理,整合 AOP、注解式 |
@Async + CompletableFuture |
异步任务返回结果并聚合 | 组合灵活,可并发+返回处理并行提升性能 |