多线程核心知识
1. 线程的创建方式
-
继承Thread
- 方法:继承
java.lang.Thread类,重写run()方法。 - 启动:创建子类实例,调用其
start()方法。 - 特点:简单,但Java单继承限制了扩展性。
class MyThread extends Thread { @Override public void run() { System.out.println("线程执行"); } } // 启动 new MyThread().start(); - 方法:继承
-
实现Runnable
- 方法:实现
java.lang.Runnable接口,实现run()方法。 - 启动:将
Runnable实例作为参数传给Thread构造器,再调用Thread实例的start()方法。 - 特点:可继承其他类。
class MyRunnable implements Runnable { @Override public void run() { System.out.println("线程执行"); } } // 启动 Thread thread = new Thread(new MyRunnable()); thread.start(); - 方法:实现
-
实现 Callable 接口
- 方法:实现
java.util.concurrent.Callable<V>接口,实现call()方法(可抛出异常、有返回值)。 - 启动:将
Callable实例提交给线程池(如ExecutorService),或封装为FutureTask再传给Thread。 - 特点:可获取异步执行结果(通过
Future/FutureTask),可捕获执行过程中的异常。
class MyCallable implements Callable<String> { @Override public String call() throws Exception { return "执行结果"; } } // 使用FutureTask启动 FutureTask<String> task = new FutureTask<>(new MyCallable()); new Thread(task).start(); String result = task.get(); // 获取返回值 - 方法:实现
Future与FutureTask简要说明:
Future它是一个接口,位于java.util.concurrent包下。用于表示异步处理的结果。它提供了一组方法来判断异步处理是否完成、获取异步处理结果,以及取消异步处理等。
FutureTask是Future的其中一个实现类,且没有额外提供新方法。CompletableFuture也是实现类之一(Java 8+)提供更强大的异步编程能力。额外提供了很多新方法。
// Future常用方法
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning); // 取消任务
boolean isCancelled(); // 是否被取消
boolean isDone(); // 是否完成(正常/异常/取消)
V get() throws InterruptedException, ExecutionException; // 阻塞获取结果(阻塞直到计算完成)
V get(long timeout, TimeUnit unit) throws ...; // 超时获取结果
}
// FutureTask构造方法
public FutureTask(Callable<V> callable)
public FutureTask(Runnable runnable, V result) // 参数2:预设的返回结果
-
使用线程池
- 方法:通过
Executors工具类或ThreadPoolExecutor创建线程池,提交Runnable或Callable任务。 - 启动:调用线程池的
execute()(提交 Runnable)或submit()(提交 Callable/Runnable)方法。 - 特点:实际开发中最常用,利于线程复用、管理和控制并发数。
// 不推荐是用Executors来创建,阿里手册有说明这个情况。 // Executors创建线程池示例: ExecutorService executor = Executors.newFixedThreadPool(5); executor.execute(() -> System.out.println("Runnable任务")); // 提交Runnable Future<String> future = executor.submit(new MyCallable()); // 提交Callable - 方法:通过
execute()与submit()简要说明:具体说明用到时再自行查阅

2. 线程安全
核心思想:保证多线程环境下共享数据的一致性和操作正确性
- 同步方法
public class Counter {
private int count = 0;
// 实例同步方法 - 锁this对象
public synchronized void increment() {
count++;
System.out.println(Thread.currentThread().getName() + ": " + count);
}
// 多个同步方法互斥:线程A执行increment()时,线程B无法执行decrement()
public synchronized void decrement() {
count--;
}
}
- 同步代码块
public class FineGrainedLock {
private int count1 = 0;
private int count2 = 0;
// 使用不同锁对象,提高并发度
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void increment1() {
synchronized(lock1) { // 只锁count1相关操作
count1++;
}
}
public void increment2() {
synchronized(lock2) { // 只锁count2相关操作
count2++;
}
// increment1和increment2可并发执行
}
public void incrementBoth() {
synchronized(lock1) { // 需要获取两把锁
synchronized(lock2) {
count1++;
count2++;
}
}
// 注意:可能产生死锁,需要固定锁顺序
}
}
- Lock锁
public class LockExample {
private final Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock(); // 手动获取锁
try {
count++;
// 可能抛出异常的代码放在try块
} finally {
lock.unlock(); // 必须finally中释放
}
}
// 错误示例:忘记unlock()
public void badIncrement() {
lock.lock();
count++;
// 如果这里抛出异常,锁永远不会释放!
// 正确做法:用try-finally包裹
}
}

3. 线程池
核心思想:管理一组工作线程的池化技术,复用线程,避免频繁创建/销毁开销。
Executor (接口)
↑
ExecutorService (接口)
↑
AbstractExecutorService (抽象类)
↑
ThreadPoolExecutor (核心实现类)
↑
ScheduledThreadPoolExecutor (定时任务)
// 核心参数(ThreadPoolExecutor 7大参数)
ThreadPoolExecutor(
int corePoolSize, // 核心线程数—————————— 根据CPU密集型/IO密集型设置
int maximumPoolSize, // 最大线程数 —————————— CPU密集型:N+1、IO密集型:2N
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 空闲线程存活时间的时间单位
BlockingQueue<Runnable> workQueue, // 任务队列 —————————— 常用ArrayBlockingQueue
// LinkedBlockingQueue 无界队列(默认Integer.MAX_VALUE),任务可能无限堆积,导致OOM
// SynchronousQueue 同步队列,不存储元素,直接传递
// ArrayBlockingQueue 有界队列,需指定容量
// PriorityBlockingQueue 优先级队列
ThreadFactory threadFactory, // 创建线程的工厂
RejectedExecutionHandler handler // 拒绝策略(队列满且线程达最大时的拒绝策略),拒绝策略是系统的最后防线,应保证系统不会因为任务被拒绝而崩溃,并提供降级方案。
// 有4种内置策略,一般实际开发时会自定义拒绝策略(实现RejectedExecutionHandler接口,重写rejectedExecution方法)
// AbortPolicy 中止策略/抛出异常策略,直接抛出RejectedExecutionException
// CallerRunsPolicy 调用者运行策略,由提交任务的线程自己执行该任务
// DiscardPolicy 丢弃策略,静默丢弃被拒绝的任务,不做任何处理
// DiscardOldestPolicy 丢弃最旧策略,丢弃队列头部任务(最先提交的任务),重试提交
)
// 自定义线程池示例:
ThreadPoolExecutor customPool = new ThreadPoolExecutor(
5, // corePoolSize
10, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100), // 有界队列,防止OOM
new ThreadFactory() { // 自定义线程工厂
private AtomicInteger count = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("自定义线程-" + count.getAndIncrement());
thread.setDaemon(false); // 非守护线程
return thread;
}
},
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
- 线程池工作原理
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数 = 2
4, // 最大线程数 = 4
60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2) // 队列容量 = 2
);
// 场景模拟:
1. 提交任务1 → 创建核心线程1执行 ✓
2. 提交任务2 → 创建核心线程2执行 ✓
3. 提交任务3 → 核心线程已满,放入队列 ✓
4. 提交任务4 → 放入队列 ✓
5. 提交任务5 → 队列已满,创建非核心线程3执行 ✓
6. 提交任务6 → 创建非核心线程4执行 ✓
7. 提交任务7 → 线程数已达最大(4),队列已满(2),执行拒绝策略 ✗
- SpringBoot整合线程池
前言:因为我们现在一般都使用springboot,而springboot提供了一个更强大的类(ThreadPoolTaskExecutor)去帮助我们整合线程池。
好处:如果你使用ThreadPoolTaskExecutor这种方式的话,能使用其提供的注解@Async。
// 1. 启动类添加@EnableAsync
@SpringBootApplication
@EnableAsync // 启用异步支持
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// 2. 创建线程池配置类。配置线程池(可选,不配置使用默认)
@Configuration
public class AsyncConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // 核心线程数
executor.setMaxPoolSize(10); // 最大线程数
executor.setQueueCapacity(100); // 队列容量
executor.setKeepAliveSeconds(60); // 线程空闲时间
executor.setThreadNamePrefix("Async-"); // 线程名前缀
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
// 3. 使用线程池
@Service
public class EmailServiceImpl implements EmailService{
// 将需要异步执行的方法用 @Async 标记
@Async("taskExecutor")
@Override
public void sendSimpleEmail(String to, String content) {
// 模拟发送邮件耗时
try {
Thread.sleep(3000);
System.out.println("发送邮件给: " + to + ", 内容: " + content);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
// 4. 调用方
@RestController
@RequestMapping("/email")
public class EmailController {
@Autowired
private EmailService emailService;
@GetMapping("/send")
public String sendEmail() {
// 立即返回,不阻塞
emailService.sendSimpleEmail("user@example.com", "测试邮件");
return "邮件发送请求已接收";
}
}
- 线程池中是否能支持事务
结论:线程池本身不提供事务管理,多线程中的事务处理需要额外设计。
最佳实践:尽量不在事务中开启多线程。
// 问题:事务方法在子线程中不生效
@Transactional
public void parentMethod() {
// 主线程事务
userDao.update(user);
// 子线程:事务会失效!
executor.execute(() -> {
orderDao.createOrder(order);
});
}
- 线程池中如何保证事务
方案一:利用completable异步编排实现
springboot整合线程池(ThreadPoolTaskExecutor),利用注解@Async开启异步处理。有事务的情况(用到的时候自行百度,有很多解决方案,以下方案没测试)
// 问题根源展示
@Service
public class OrderService {
@Async // 新线程执行
@Transactional // 期望有事务,但实际上...
public void createOrder(Order order) {
// 在多线程中:@Transactional注解失效!
// 原因:事务基于ThreadLocal,而新线程无法获取父线程的事务上下文
orderDao.save(order);
inventoryDao.deduct(order.getProductId(), order.getQuantity());
// 如果这里异常,不会回滚!
}
}

PS:前两种方案都无法直接实现跨线程的事务原子性。必须通过业务设计(发生异常后,写补偿逻辑)来实现原子性。后两种推荐最后一种

4. 异步编排
CompletableFuture是 Java 8 引入的异步编程工具, 实现了 Future 和 CompletionStage 接口,提供了更强大的异步编程能力,支持链式调用、组合多个异步任务、异常处理等功能。
浙公网安备 33010602011771号