Java多线程详解
进程(Process):操作系统分配资源的最小单位一个进程就是一个运行中的程序例如:一个正在运行的IDEA就是一个进程线程(Thread):CPU调度的最小单位一个进程可以包含多个线程例如:IDEA进程中,一个线程负责代码编辑,一个线程负责代码编译,一个线程负责代码提示关系:进程是线程的容器一个进程至少有一个线程(主线程)同一进程内的线程共享该进程的资源(内存、文件句柄等)│进程 (JVM)│
本文系统讲解Java多线程编程,从线程基础到并发工具类,覆盖线程创建、线程安全、锁机制、线程池、并发容器、原子类、CompletableFuture等全链路知识,配合大量实战代码,帮助你全面掌握Java并发编程。
目录
1. 什么是多线程
1.1 进程与线程
进程(Process): 操作系统分配资源的最小单位 一个进程就是一个运行中的程序 例如:一个正在运行的IDEA就是一个进程 线程(Thread): CPU调度的最小单位 一个进程可以包含多个线程 例如:IDEA进程中,一个线程负责代码编辑,一个线程负责代码编译,一个线程负责代码提示 关系: 进程是线程的容器 一个进程至少有一个线程(主线程) 同一进程内的线程共享该进程的资源(内存、文件句柄等)
┌─────────────────────────────────────────────┐ │ 进程 (JVM) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ 线程1 │ │ 线程2 │ │ 线程3 │ │ │ │ main() │ │ 处理请求 │ │ 定时任务 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ 共享区域:堆内存、方法区、静态变量 │ │ 独立区域:栈内存、程序计数器 │ └─────────────────────────────────────────────┘
1.2 为什么需要多线程
场景1:提高CPU利用率 单线程:CPU等待IO时处于空闲状态 多线程:一个线程等待IO时,CPU可以执行其他线程 场景2:提高响应速度 Web服务器:一个请求一个线程,多个请求并发处理 不用多线程:请求排队处理,用户体验极差 场景3:充分利用多核CPU 单线程只能使用一个CPU核心 多线程可以同时使用多个CPU核心
1.3 并行与并发
并发(Concurrency): 多个任务交替执行(单核CPU上的"同时") 实际上是快速切换,宏观上看起来同时执行 并行(Parallelism): 多个任务真正同时执行(多核CPU) 每个核心执行一个任务 并发:一个人交替做两件事 并行:两个人同时做两件事
2. 线程的创建方式
2.1 方式一:继承Thread类
/**
* 方式1:继承Thread类
* 重写run()方法
*/
public class MyThread extends Thread {
@Override
public void run() {
// 线程执行的代码
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
Thread.sleep(500); // 休眠500ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 创建线程对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
// 设置线程名
t1.setName("线程A");
t2.setName("线程B");
// 启动线程(不是调用run(),而是调用start())
t1.start();
t2.start();
// 注意:
// t.start() → 启动新线程,run()在新线程中执行
// t.run() → 只是普通方法调用,不会启动新线程
}
}
2.2 方式二:实现Runnable接口
/**
* 方式2:实现Runnable接口
* 推荐使用,因为Java只支持单继承,实现接口更灵活
*/
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " - " + i);
}
}
public static void main(String[] args) {
// 创建Runnable对象
MyRunnable task = new MyRunnable();
// 将Runnable传给Thread
Thread t1 = new Thread(task, "线程A");
Thread t2 = new Thread(task, "线程B");
t1.start();
t2.start();
}
}
2.3 方式三:实现Callable接口(有返回值)
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
* 方式3:实现Callable接口
* 可以有返回值,可以抛出异常
*/
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 可以有返回值
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
return sum;
}
public static void main(String[] args) throws Exception {
// 创建Callable对象
MyCallable callable = new MyCallable();
// 用FutureTask包装Callable
FutureTask<Integer> futureTask = new FutureTask<>(callable);
// 创建线程
Thread thread = new Thread(futureTask, "计算线程");
thread.start();
// 获取返回值(会阻塞当前线程直到计算完成)
Integer result = futureTask.get();
System.out.println("计算结果: " + result); // 5050
}
}
2.4 方式四:线程池创建(最常用)
import java.util.concurrent.*;
/**
* 方式4:使用线程池
* 生产环境最推荐的方式,避免频繁创建销毁线程
*/
public class ThreadPoolDemo {
public static void main(String[] args) throws Exception {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交Runnable任务
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 执行Runnable任务");
});
// 提交Callable任务
Future<String> future = executor.submit(() -> {
return Thread.currentThread().getName() + " 执行Callable任务";
});
System.out.println(future.get());
// 关闭线程池
executor.shutdown();
}
}
2.5 四种方式对比
| 方式 | 返回值 | 异常处理 | 灵活性 | 使用场景 |
|---|---|---|---|---|
| 继承Thread | 无 | 不能抛受检异常 | 低(单继承) | 简单场景 |
| 实现Runnable | 无 | 不能抛受检异常 | 高 | 无返回值的场景 |
| 实现Callable | 有 | 可以抛受检异常 | 高 | 需要返回值的场景 |
| 线程池 | 有/无 | 可以 | 最高 | 生产环境首选 |
3. 线程的生命周期
Java线程有6种状态,定义在 Thread.State 枚举中。
start() NEW ──────────────────────────────→ RUNNABLE (新建) (可运行) │ ┌─────────────┼─────────────┐ │ │ │ ▼ ▼ ▼ BLOCKED WAITING TIMED_WAITING (阻塞) (无限等待) (超时等待) 等锁 wait() sleep(ms) synchronized join() wait(ms) LockSupport join(ms) .park() │ │ │ └─────────────┼─────────────┘ │ ▼ RUNNABLE │ ▼ TERMINATED (终止)
各状态详解
public class ThreadStateDemo {
public static void main(String[] args) throws Exception {
// 1. NEW:线程被创建但尚未启动
Thread thread = new Thread(() -> {
System.out.println("线程运行中");
});
System.out.println("创建后: " + thread.getState()); // NEW
// 2. RUNNABLE:线程正在运行或等待CPU调度
thread.start();
System.out.println("启动后: " + thread.getState()); // RUNNABLE
// 3. TIMED_WAITING:线程在有限时间内等待
Thread.sleep(1000); // 当前线程进入TIMED_WAITING
// 4. WAITING:线程无限期等待
Object lock = new Object();
synchronized (lock) {
lock.wait(); // 进入WAITING,等待notify()
}
// 5. BLOCKED:线程等待获取监视器锁
// 等待进入synchronized块时进入BLOCKED
// 6. TERMINATED:线程执行完毕
thread.join(); // 等待thread执行完毕
System.out.println("结束后: " + thread.getState()); // TERMINATED
}
}
4. 线程的常用方法
4.1 基础方法
public class ThreadMethodsDemo {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}, "工作线程");
// start() - 启动线程
thread.start();
// getName() / setName() - 获取/设置线程名
System.out.println(thread.getName()); // 工作线程
// isAlive() - 判断线程是否存活
System.out.println(thread.isAlive()); // true
// join() - 等待该线程执行完毕
thread.join(); // 当前线程阻塞,直到thread执行完毕
// join(millis) - 最多等待指定毫秒数
// thread.join(5000); // 最多等5秒
// Thread.sleep(millis) - 当前线程休眠
Thread.sleep(1000); // 休眠1秒
// Thread.yield() - 让出CPU时间片
// 提示调度器当前线程愿意让出CPU,但调度器可以忽略这个提示
Thread.yield();
// interrupt() - 中断线程
Thread t = new Thread(() -> {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// 收到中断信号
System.out.println("被中断了");
}
});
t.start();
t.interrupt(); // 发送中断信号
// setDaemon(true) - 设置为守护线程
// 守护线程在所有非守护线程结束后自动退出
// 例如:GC线程就是守护线程
Thread daemon = new Thread(() -> {
while (true) {
// 后台守护任务
}
});
daemon.setDaemon(true);
daemon.start();
// setPriority() - 设置优先级(1-10)
// 优先级只是建议,不保证执行顺序
thread.setPriority(Thread.MAX_PRIORITY); // 10
}
}
4.2 wait/notify(线程间通信)
/**
* wait/notify 实现生产者-消费者模式
*/
public class ProducerConsumerDemo {
private static final Queue<Integer> queue = new LinkedList<>();
private static final int MAX_SIZE = 5;
public static void main(String[] args) {
// 生产者
Thread producer = new Thread(() -> {
int value = 0;
while (true) {
synchronized (queue) {
while (queue.size() >= MAX_SIZE) {
try {
System.out.println("队列满了,生产者等待...");
queue.wait(); // 释放锁,进入等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.offer(value);
System.out.println("生产: " + value + ",队列大小: " + queue.size());
value++;
queue.notifyAll(); // 通知消费者
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 消费者
Thread consumer = new Thread(() -> {
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
try {
System.out.println("队列空了,消费者等待...");
queue.wait(); // 释放锁,进入等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int value = queue.poll();
System.out.println("消费: " + value + ",队列大小: " + queue.size());
queue.notifyAll(); // 通知生产者
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producer.start();
consumer.start();
}
}
wait/notify注意事项:
1. 必须在synchronized块中调用(必须持有锁) 2. wait()会释放锁,sleep()不会释放锁 3. notify()随机唤醒一个等待线程,notifyAll()唤醒所有等待线程 4. 被唤醒后需要重新获取锁才能继续执行 5. 使用while循环检查条件(不用if),防止虚假唤醒
5. 线程安全问题
5.1 什么是线程安全
/**
* 线程安全问题演示
* 多个线程同时修改共享变量,导致结果不正确
*/
public class ThreadUnsafeDemo {
private static int count = 0;
public static void main(String[] args) throws Exception {
// 创建10个线程,每个线程对count加10000次
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 10000; j++) {
count++; // 非原子操作!
}
});
threads[i].start();
}
// 等待所有线程执行完毕
for (Thread t : threads) {
t.join();
}
// 期望结果:100000
// 实际结果:可能是 87432、91256 等不确定的值
System.out.println("count = " + count);
}
}
5.2 为什么count++不是原子操作
count++ 实际上包含三个步骤: 1. 读取:从主内存读取count的值到工作内存 2. 修改:在工作内存中对count加1 3. 写入:将修改后的值写回主内存 线程安全问题的根源: 线程A:读取count = 0 线程B:读取count = 0 ← 线程A还没写回,B就读了 线程A:count = 0 + 1 = 1,写回count = 1 线程B:count = 0 + 1 = 1,写回count = 1 结果:加了两次,count只增加了1
5.3 线程安全问题的三大根源
1. 原子性(Atomicity) 操作被中断,其他线程看到中间状态 例如:count++ 被其他线程打断 2. 可见性(Visibility) 一个线程修改了共享变量,其他线程看不到最新值 原因:CPU缓存、指令重排序 3. 有序性(Ordering) 代码执行顺序与编写顺序不一致 原因:编译器优化、CPU指令重排序
6. synchronized关键字
synchronized是Java内置的互斥锁,保证同一时刻只有一个线程执行临界区代码。
6.1 三种用法
public class SynchronizedDemo {
private int count = 0;
private static int staticCount = 0;
private final Object lock = new Object();
/**
* 1. 修饰实例方法:锁的是this对象
*/
public synchronized void increment() {
count++;
}
/**
* 2. 修饰静态方法:锁的是Class对象
*/
public synchronized static void staticIncrement() {
staticCount++;
}
/**
* 3. 修饰代码块:锁的是指定对象
*/
public void blockIncrement() {
synchronized (lock) {
count++;
}
}
/**
* 修复线程安全问题
*/
public static void main(String[] args) throws Exception {
SynchronizedDemo demo = new SynchronizedDemo();
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 10000; j++) {
demo.increment(); // 线程安全
}
});
threads[i].start();
}
for (Thread t : threads) {
t.join();
}
// 结果一定是100000
System.out.println("count = " + demo.count);
}
}
6.2 synchronized的特性
1. 互斥性:同一时刻只有一个线程能持有锁 2. 可重入性:同一线程可以重复获取同一把锁(不会自己死锁) 3. 非公平性:不保证等待时间最长的线程先获取锁 4. 自动释放:退出synchronized块或方法时自动释放锁
/**
* 可重入性演示
*/
public class ReentrantDemo {
private synchronized void methodA() {
System.out.println("methodA");
methodB(); // 同一线程可以再次获取this锁
}
private synchronized void methodB() {
System.out.println("methodB");
}
// 如果synchronized不可重入,methodA调用methodB时就会死锁
}
7. volatile关键字
volatile保证变量的可见性和有序性,但不保证原子性。
7.1 可见性
/**
* volatile保证可见性
*/
public class VolatileDemo {
// 不加volatile:线程B可能永远看不到flag的变化
// 加了volatile:线程B能立即看到flag的变化
private static volatile boolean flag = false;
public static void main(String[] args) {
// 线程A:修改flag
new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("flag已修改为true");
}).start();
// 线程B:读取flag
new Thread(() -> {
while (!flag) {
// 不加volatile:可能永远在这里循环(线程B读到的是缓存中的旧值)
// 加了volatile:flag变化后线程B能立即看到
}
System.out.println("检测到flag变化");
}).start();
}
}
7.2 volatile不保证原子性
/**
* volatile不保证原子性
*/
public class VolatileAtomicDemo {
private static volatile int count = 0;
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 10000; j++) {
count++; // 即使加了volatile,结果仍然不正确
}
});
threads[i].start();
}
for (Thread t : threads) {
t.join();
}
// 结果不一定是100000
// volatile只保证可见性,不保证count++的原子性
System.out.println("count = " + count);
}
}
7.3 volatile适用场景
适用: 1. 状态标志(如上面的flag示例) 2. 双重检查锁定的单例模式 3. 读多写少的场景 不适用: 1. 需要原子性的操作(如count++) 2. 复合操作(如 check-then-act)
7.4 双重检查锁定单例
/**
* volatile在单例模式中的应用
*/
public class Singleton {
// volatile防止指令重排序
// new Singleton() 实际上分三步:
// 1. 分配内存空间
// 2. 初始化对象
// 3. 将引用指向内存空间
// 如果不加volatile,步骤2和3可能重排,导致其他线程拿到未初始化的对象
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查(不加锁)
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查(加锁)
instance = new Singleton();
}
}
}
return instance;
}
}
8. ReentrantLock
ReentrantLock是java.util.concurrent包中的锁,比synchronized更灵活。
8.1 基本用法
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock(); // 必须在finally中释放
}
}
/**
* 尝试获取锁(非阻塞)
*/
public boolean tryIncrement() {
if (lock.tryLock()) {
try {
count++;
return true;
} finally {
lock.unlock();
}
}
return false;
}
/**
* 带超时的尝试获取锁
*/
public boolean tryIncrementWithTimeout() throws InterruptedException {
if (lock.tryLock(3, java.util.concurrent.TimeUnit.SECONDS)) {
try {
count++;
return true;
} finally {
lock.unlock();
}
}
return false;
}
}
8.2 公平锁与非公平锁
// 非公平锁(默认):新来的线程可以插队,吞吐量高 ReentrantLock unfairLock = new ReentrantLock(false); // 公平锁:严格按照请求顺序获取锁,吞吐量低 ReentrantLock fairLock = new ReentrantLock(true);
8.3 可中断锁
public class InterruptibleLockDemo {
private final ReentrantLock lock = new ReentrantLock();
public void doWork() throws InterruptedException {
// lockInterruptibly():等待锁的过程中可以被中断
lock.lockInterruptibly();
try {
// 业务逻辑
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
InterruptibleLockDemo demo = new InterruptibleLockDemo();
Thread t1 = new Thread(() -> {
try {
demo.doWork();
} catch (InterruptedException e) {
System.out.println("t1被中断,放弃获取锁");
}
});
Thread t2 = new Thread(() -> {
try {
demo.doWork();
} catch (InterruptedException e) {
System.out.println("t2被中断,放弃获取锁");
}
});
t1.start();
t2.start();
// 中断t2
t2.interrupt();
}
}
8.4 Condition条件变量
import java.util.concurrent.locks.*;
import java.util.LinkedList;
import java.util.Queue;
/**
* Condition实现生产者-消费者
* 比wait/notify更灵活,可以创建多个条件
*/
public class ConditionDemo {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition(); // 队列未满
private final Condition notEmpty = lock.newCondition(); // 队列非空
private final Queue<Integer> queue = new LinkedList<>();
private static final int MAX_SIZE = 5;
// 生产者
public void produce(int value) throws InterruptedException {
lock.lock();
try {
while (queue.size() >= MAX_SIZE) {
notFull.await(); // 队列满了,等待"未满"条件
}
queue.offer(value);
System.out.println("生产: " + value);
notEmpty.signal(); // 通知消费者
} finally {
lock.unlock();
}
}
// 消费者
public int consume() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 队列空了,等待"非空"条件
}
int value = queue.poll();
System.out.println("消费: " + value);
notFull.signal(); // 通知生产者
return value;
} finally {
lock.unlock();
}
}
}
9. 线程通信
9.1 使用BlockingQueue(最推荐)
import java.util.concurrent.*;
/**
* 使用BlockingQueue实现生产者-消费者
* BlockingQueue内部已经实现了线程同步,不需要手动加锁
*/
public class BlockingQueueDemo {
public static void main(String[] args) {
// 有界阻塞队列
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
// 生产者
Thread producer = new Thread(() -> {
int value = 0;
try {
while (true) {
queue.put(value); // 队列满时自动阻塞
System.out.println("生产: " + value);
value++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 消费者
Thread consumer = new Thread(() -> {
try {
while (true) {
int value = queue.take(); // 队列空时自动阻塞
System.out.println("消费: " + value);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
}
}
9.2 常用BlockingQueue实现
| 实现类 | 特点 | 使用场景 |
|---|---|---|
| ArrayBlockingQueue | 有界数组,公平/非公平可选 | 固定大小的缓冲区 |
| LinkedBlockingQueue | 有界链表,默认非公平 | 吞吐量要求高的场景 |
| PriorityBlockingQueue | 无界优先级队列 | 需要按优先级处理任务 |
| SynchronousQueue | 不存储元素,直接传递 | 直接交付场景 |
| DelayQueue | 延迟获取,元素到期才能取 | 定时任务、缓存过期 |
10. 线程池
线程池是Java并发编程中最核心的组件,生产环境必须使用线程池。
10.1 为什么需要线程池
不使用线程池的问题: 每个任务创建一个新线程 → 线程创建销毁开销大 10000个任务 → 10000个线程 → 内存溢出 无法控制并发数量 → 可能把服务器压垮 线程池的好处: 1. 复用线程,减少创建销毁开销 2. 控制并发数量,防止资源耗尽 3. 统一管理、监控线程 4. 提供任务队列、拒绝策略等功能
10.2 创建线程池
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) {
// ============ 方式1:使用Executors工具类(不推荐用于生产) ============
// 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(3);
// 缓存线程池(线程数不限,空闲线程60秒后回收)
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 单线程池(保证任务顺序执行)
ExecutorService singlePool = Executors.newSingleThreadExecutor();
// 定时线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);
// ============ 方式2:手动创建ThreadPoolExecutor(推荐) ============
ThreadPoolExecutor pool = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
60L, // 非核心线程空闲存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(100), // 任务队列(容量100)
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
}
}
10.3 ThreadPoolExecutor参数详解
┌─────────────────────────────────────────────────────────────┐ │ ThreadPoolExecutor工作原理 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 提交任务 │ │ │ │ │ ▼ │ │ 当前线程数 < 核心线程数? │ │ │ │ │ 是 → 创建核心线程执行任务 │ │ 否 → 任务队列未满? │ │ │ │ │ 是 → 放入任务队列等待 │ │ 否 → 当前线程数 < 最大线程数? │ │ │ │ │ 是 → 创建非核心线程执行任务 │ │ 否 → 执行拒绝策略 │ │ │ └─────────────────────────────────────────────────────────────┘ 参数说明: corePoolSize 核心线程数(一直存活,即使空闲) maximumPoolSize 最大线程数(核心线程 + 非核心线程) keepAliveTime 非核心线程空闲存活时间 workQueue 任务队列(存放等待执行的任务) threadFactory 线程工厂(自定义线程名等) handler 拒绝策略(任务队列满且线程数达到最大时的处理方式)
10.4 四种拒绝策略
// 1. AbortPolicy(默认):抛出RejectedExecutionException new ThreadPoolExecutor.AbortPolicy(); // 2. CallerRunsPolicy:由调用线程(提交任务的线程)执行该任务 new ThreadPoolExecutor.CallerRunsPolicy(); // 3. DiscardPolicy:默默丢弃任务,不抛异常 new ThreadPoolExecutor.DiscardPolicy(); // 4. DiscardOldestPolicy:丢弃队列中最老的任务,然后重新提交当前任务 new ThreadPoolExecutor.DiscardOldestPolicy();
10.5 提交任务
import java.util.concurrent.*;
public class SubmitTaskDemo {
public static void main(String[] args) throws Exception {
ThreadPoolExecutor pool = new ThreadPoolExecutor(
2, 5, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100)
);
// 1. execute():提交Runnable,无返回值
pool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 执行任务");
});
// 2. submit():提交Callable,返回Future
Future<String> future = pool.submit(() -> {
return "任务结果";
});
String result = future.get(); // 阻塞等待结果
System.out.println(result);
// 3. invokeAll():批量提交,等待全部完成
List<Callable<String>> tasks = List.of(
() -> "任务1",
() -> "任务2",
() -> "任务3"
);
List<Future<String>> futures = pool.invokeAll(tasks);
for (Future<String> f : futures) {
System.out.println(f.get());
}
// 4. invokeAny():批量提交,返回最先完成的结果
String firstResult = pool.invokeAny(tasks);
System.out.println("最先完成: " + firstResult);
// 关闭线程池
pool.shutdown(); // 不再接受新任务,等待已提交任务完成
// pool.shutdownNow(); // 尝试中断所有正在执行的任务
pool.awaitTermination(60, TimeUnit.SECONDS); // 等待所有任务完成
}
}
10.6 线程池配置建议
CPU密集型任务(计算、加密、压缩): 线程数 = CPU核心数 + 1 例如:8核CPU → 9个线程 IO密集型任务(网络请求、数据库查询、文件读写): 线程数 = CPU核心数 × 2 或者:线程数 = CPU核心数 / (1 - 阻塞系数) 阻塞系数一般在0.8~0.9之间 例如:8核CPU → 16~80个线程 混合型任务: 计算IO密集型和CPU密集型的比例,取折中值 实际项目中: 通常先设置一个经验值,然后通过压测调整 建议使用有界队列,防止OOM
// 获取CPU核心数 int cpuCores = Runtime.getRuntime().availableProcessors(); // CPU密集型 ThreadPoolExecutor cpuPool = new ThreadPoolExecutor( cpuCores + 1, cpuCores + 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100) ); // IO密集型 ThreadPoolExecutor ioPool = new ThreadPoolExecutor( cpuCores * 2, cpuCores * 2, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100) );
10.7 线程池监控
ThreadPoolExecutor pool = new ThreadPoolExecutor(
2, 5, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100)
);
// 核心线程数
pool.getCorePoolSize();
// 最大线程数
pool.getMaximumPoolSize();
// 当前活跃线程数
pool.getActiveCount();
// 已完成任务数
pool.getCompletedTaskCount();
// 总任务数
pool.getTaskCount();
// 队列中的任务数
pool.getQueue().size();
// 池中当前线程数
pool.getPoolSize();
11. 并发容器
11.1 ConcurrentHashMap
import java.util.concurrent.ConcurrentHashMap;
/**
* 线程安全的HashMap
* JDK8使用CAS + synchronized实现,性能远好于Hashtable和Collections.synchronizedMap
*/
public class ConcurrentHashMapDemo {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 基本操作(线程安全)
map.put("key1", 1);
map.get("key1");
map.remove("key1");
// 原子操作
map.putIfAbsent("key1", 1); // 不存在才放入
map.computeIfAbsent("key2", k -> 2); // 不存在才计算
map.merge("key1", 1, Integer::sum); // 合并值(原子操作)
// 批量操作(JDK8+)
map.forEach(10, (key, value) -> {
System.out.println(key + "=" + value);
});
// 搜索(JDK8+)
String result = map.search(10, (key, value) -> {
if (value > 100) return key;
return null;
});
}
}
11.2 CopyOnWriteArrayList
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 写时复制的ArrayList
* 写操作时复制整个数组,适合读多写少的场景
*/
public class CopyOnWriteArrayListDemo {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// 写操作(加锁,复制数组)
list.add("A");
list.add("B");
// 读操作(不加锁,直接读)
String first = list.get(0);
// 迭代时不会抛ConcurrentModificationException
for (String item : list) {
System.out.println(item);
}
}
}
11.3 并发容器对比
| 容器 | 线程安全 | 适用场景 |
|---|---|---|
| HashMap | 不安全 | 单线程 |
| Hashtable | 安全(全表锁) | 不推荐,性能差 |
| Collections.synchronizedMap | 安全(全表锁) | 不推荐,性能差 |
| ConcurrentHashMap | 安全(分段锁/CAS) | 并发Map首选 |
| ArrayList | 不安全 | 单线程 |
| Vector | 安全(全表锁) | 不推荐 |
| CopyOnWriteArrayList | 安全(写时复制) | 读多写少 |
12. 原子类
原子类基于CAS(Compare And Swap)实现无锁线程安全。
12.1 基本原子类
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicBoolean;
public class AtomicDemo {
// AtomicInteger:原子整数
private static AtomicInteger count = new AtomicInteger(0);
// AtomicLong:原子长整数
private static AtomicLong total = new AtomicLong(0L);
// AtomicBoolean:原子布尔
private static AtomicBoolean flag = new AtomicBoolean(false);
public static void main(String[] args) throws Exception {
// 基本操作
count.set(10); // 设置值
int val = count.get(); // 获取值
count.incrementAndGet(); // 先加1再获取(++i)
count.getAndIncrement(); // 先获取再加1(i++)
count.addAndGet(5); // 先加5再获取
count.decrementAndGet(); // 先减1再获取
// CAS操作
boolean success = count.compareAndSet(10, 20);
// 如果当前值是10,则更新为20,返回true
// 如果当前值不是10,则不更新,返回false
// 原子更新
count.updateAndGet(x -> x * 2); // 原子地将值翻倍
count.accumulateAndGet(5, Integer::sum); // 原子地加5
// ============ 解决线程安全问题 ============
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 10000; j++) {
count.incrementAndGet(); // 线程安全
}
});
threads[i].start();
}
for (Thread t : threads) {
t.join();
}
// 结果一定是100000
System.out.println("count = " + count.get());
}
}
12.2 数组原子类
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
AtomicIntegerArray atomicArr = new AtomicIntegerArray(arr);
atomicArr.incrementAndGet(0); // 第0个元素加1
atomicArr.addAndGet(1, 10); // 第1个元素加10
System.out.println(atomicArr.get(0)); // 2
System.out.println(atomicArr.get(1)); // 12
// 原数组不受影响
System.out.println(arr[0]); // 1
}
}
12.3 引用原子类
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* AtomicReference:原子引用
* AtomicStampedReference:带版本号的原子引用(解决ABA问题)
*/
public class AtomicReferenceDemo {
// AtomicReference
private static AtomicReference<String> ref = new AtomicReference<>("初始值");
// AtomicStampedReference(解决ABA问题)
private static AtomicStampedReference<Integer> stampedRef =
new AtomicStampedReference<>(100, 0); // 初始值100,版本号0
public static void main(String[] args) {
// AtomicReference
ref.compareAndSet("初始值", "新值");
// AtomicStampedReference
int[] stampHolder = new int[1];
int value = stampedRef.get(stampHolder); // 获取值和版本号
int stamp = stampHolder[0];
// CAS同时检查值和版本号
stampedRef.compareAndSet(100, 200, stamp, stamp + 1);
}
}
13. CountDownLatch与CyclicBarrier
13.1 CountDownLatch(倒计时门栓)
import java.util.concurrent.CountDownLatch;
/**
* CountDownLatch:允许一个或多个线程等待其他线程完成
* 一次性使用,计数到0后不能重置
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws Exception {
// 创建计数器,初始值为3
CountDownLatch latch = new CountDownLatch(3);
// 启动3个子线程
for (int i = 1; i <= 3; i++) {
final int taskId = i;
new Thread(() -> {
try {
Thread.sleep((long) (Math.random() * 3000));
System.out.println("任务" + taskId + " 完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // 计数减1
}
}).start();
}
// 主线程等待计数器归零
latch.await(); // 阻塞直到计数为0
System.out.println("所有任务完成,继续执行");
}
}
13.2 CyclicBarrier(循环屏障)
import java.util.concurrent.CyclicBarrier;
/**
* CyclicBarrier:所有线程到达屏障点后一起继续执行
* 可以重复使用(reset)
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
// 3个线程都到达屏障后,执行barrierAction
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有线程已就绪,开始执行!");
});
for (int i = 1; i <= 3; i++) {
final int threadId = i;
new Thread(() -> {
try {
System.out.println("线程" + threadId + " 准备就绪");
barrier.await(); // 等待其他线程
System.out.println("线程" + threadId + " 开始执行");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
13.3 对比
| 对比项 | CountDownLatch | CyclicBarrier |
|---|---|---|
| 计数方式 | 计数递减到0 | 计数递增到目标值 |
| 可重用 | 不可以 | 可以(reset) |
| 等待方 | 一个或多个线程等待 | 所有线程互相等待 |
| 触发条件 | 计数为0 | 所有线程到达屏障点 |
| 典型场景 | 主线程等待子线程完成 | 多线程同时开始执行 |
14. CompletableFuture异步编程
CompletableFuture是JDK8引入的异步编程工具,支持链式调用、组合、异常处理。
14.1 基本用法
import java.util.concurrent.CompletableFuture;
public class CompletableFutureDemo {
public static void main(String[] args) throws Exception {
// 1. 异步执行(无返回值)
CompletableFuture<Void> voidFuture = CompletableFuture.runAsync(() -> {
System.out.println("异步执行: " + Thread.currentThread().getName());
});
// 2. 异步执行(有返回值)
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "异步结果";
});
// 获取结果
String result = future.get(); // 阻塞等待
System.out.println(result);
// 3. 使用自定义线程池
java.util.concurrent.ExecutorService pool =
java.util.concurrent.Executors.newFixedThreadPool(4);
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
return "自定义线程池结果";
}, pool); // 指定线程池
System.out.println(future2.get());
pool.shutdown();
}
}
14.2 链式调用
import java.util.concurrent.CompletableFuture;
public class ChainDemo {
public static void main(String[] args) throws Exception {
// thenApply:转换结果(同步)
CompletableFuture<String> future1 = CompletableFuture
.supplyAsync(() -> 100)
.thenApply(value -> value * 2) // 200
.thenApply(value -> "结果: " + value); // "结果: 200"
System.out.println(future1.get());
// thenApplyAsync:转换结果(异步)
CompletableFuture<String> future2 = CompletableFuture
.supplyAsync(() -> "hello")
.thenApplyAsync(s -> s.toUpperCase()); // 在新线程中执行
System.out.println(future2.get());
// thenAccept:消费结果(无返回值)
CompletableFuture<Void> future3 = CompletableFuture
.supplyAsync(() -> "hello")
.thenAccept(s -> System.out.println("收到: " + s));
// thenRun:执行后续动作(不关心前面的结果)
CompletableFuture<Void> future4 = CompletableFuture
.supplyAsync(() -> "hello")
.thenRun(() -> System.out.println("前面的任务完成了"));
future4.get();
}
}
14.3 组合多个异步任务
import java.util.concurrent.CompletableFuture;
public class CombineDemo {
public static void main(String[] args) throws Exception {
// ============ thenCompose:串行组合(第二个任务依赖第一个的结果) ============
CompletableFuture<String> composed = CompletableFuture
.supplyAsync(() -> "userId")
.thenCompose(userId -> getUserOrders(userId)); // 用userId查询订单
System.out.println(composed.get());
// ============ thenCombine:并行组合(两个任务同时执行,合并结果) ============
CompletableFuture<Double> combined = CompletableFuture
.supplyAsync(() -> 100.0) // 任务1:获取价格
.thenCombine(
CompletableFuture.supplyAsync(() -> 0.8), // 任务2:获取折扣
(price, discount) -> price * discount // 合并:计算折后价
);
System.out.println("折后价: " + combined.get());
// ============ allOf:等待所有任务完成 ============
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "任务1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "任务2");
CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> "任务3");
CompletableFuture.allOf(task1, task2, task3).join(); // 等待全部完成
System.out.println(task1.get() + ", " + task2.get() + ", " + task3.get());
// ============ anyOf:任意一个完成即可 ============
CompletableFuture<Object> anyResult = CompletableFuture.anyOf(task1, task2, task3);
System.out.println("最先完成: " + anyResult.get());
}
static CompletableFuture<String> getUserOrders(String userId) {
return CompletableFuture.supplyAsync(() -> userId + "的订单列表");
}
}
14.4 异常处理
import java.util.concurrent.CompletableFuture;
public class ExceptionDemo {
public static void main(String[] args) throws Exception {
// handle:处理结果或异常
CompletableFuture<String> future1 = CompletableFuture
.supplyAsync(() -> {
if (true) throw new RuntimeException("出错了");
return "正常结果";
})
.handle((result, ex) -> {
if (ex != null) {
return "默认值(异常: " + ex.getMessage() + ")";
}
return result;
});
System.out.println(future1.get());
// exceptionally:只处理异常
CompletableFuture<String> future2 = CompletableFuture
.supplyAsync(() -> {
throw new RuntimeException("出错了");
})
.exceptionally(ex -> {
return "降级结果";
});
System.out.println(future2.get());
// whenComplete:回调(不改变结果)
CompletableFuture<String> future3 = CompletableFuture
.supplyAsync(() -> "正常结果")
.whenComplete((result, ex) -> {
if (ex != null) {
System.out.println("异常: " + ex.getMessage());
} else {
System.out.println("成功: " + result);
}
});
System.out.println(future3.get());
}
}
14.5 实战:并行调用多个接口
import java.util.concurrent.CompletableFuture;
import java.util.List;
import java.util.stream.Collectors;
/**
* 实战场景:同时调用多个微服务接口,汇总结果
*/
public class ParallelCallDemo {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
// 并行调用3个接口
CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() -> {
sleep(1000); // 模拟调用用户服务
return "用户信息";
});
CompletableFuture<String> orderFuture = CompletableFuture.supplyAsync(() -> {
sleep(1500); // 模拟调用订单服务
return "订单信息";
});
CompletableFuture<String> productFuture = CompletableFuture.supplyAsync(() -> {
sleep(800); // 模拟调用商品服务
return "商品信息";
});
// 等待全部完成
CompletableFuture.allOf(userFuture, orderFuture, productFuture).join();
// 汇总结果
String result = String.join(", ",
userFuture.get(), orderFuture.get(), productFuture.get());
long cost = System.currentTimeMillis() - start;
System.out.println("结果: " + result);
System.out.println("耗时: " + cost + "ms");
// 串行需要 1000+1500+800 = 3300ms
// 并行只需 max(1000,1500,800) ≈ 1500ms
}
private static void sleep(long millis) {
try { Thread.sleep(millis); } catch (InterruptedException e) { }
}
}
15. ThreadLocal
ThreadLocal为每个线程提供独立的变量副本,实现线程隔离。
15.1 基本用法
public class ThreadLocalDemo {
// 每个线程有独立的副本
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
public static void main(String[] args) {
// 线程A
new Thread(() -> {
threadLocal.set(100);
System.out.println("线程A: " + threadLocal.get()); // 100
threadLocal.remove(); // 用完必须remove,防止内存泄漏
}).start();
// 线程B
new Thread(() -> {
threadLocal.set(200);
System.out.println("线程B: " + threadLocal.get()); // 200
threadLocal.remove();
}).start();
// 主线程
System.out.println("主线程: " + threadLocal.get()); // 0(默认值)
}
}
15.2 典型应用:用户信息传递
/**
* 使用ThreadLocal在请求处理链路中传递用户信息
* 例如:在Controller中设置,在Service中获取
*/
public class UserContext {
private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
public static void setUser(User user) {
currentUser.set(user);
}
public static User getUser() {
return currentUser.get();
}
public static void clear() {
currentUser.remove();
}
}
// 拦截器中设置
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, ...) {
String token = request.getHeader("Authorization");
User user = validateToken(token);
UserContext.setUser(user); // 设置到ThreadLocal
return true;
}
@Override
public void afterCompletion(...) {
UserContext.clear(); // 请求结束必须清理
}
}
// Service中使用
@Service
public class OrderService {
public void createOrder() {
User user = UserContext.getUser(); // 直接获取当前用户
// 创建订单...
}
}
15.3 ThreadLocal内存泄漏
ThreadLocal内存泄漏的原因: Thread对象中有一个ThreadLocalMap key是ThreadLocal对象(弱引用) value是我们设置的值(强引用) 当ThreadLocal对象被GC回收后: key变成null 但value仍然被强引用,无法被GC回收 → 内存泄漏! 解决方案: 使用完毕后调用 threadLocal.remove() 特别是在线程池中,线程会被复用,必须手动清理
16. 实战:多线程下载文件
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.*;
/**
* 多线程下载文件
* 将文件分成多个块,每个线程下载一个块,最后合并
*/
public class MultiThreadDownload {
private static final int THREAD_COUNT = 4; // 线程数
private static final String FILE_URL = "https://example.com/large-file.zip";
private static final String OUTPUT_FILE = "large-file.zip";
public static void main(String[] args) throws Exception {
// 1. 获取文件大小
long fileSize = getFileSize(FILE_URL);
System.out.println("文件大小: " + fileSize + " bytes");
// 2. 计算每个线程下载的范围
long blockSize = fileSize / THREAD_COUNT;
// 3. 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(THREAD_COUNT);
CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
// 4. 启动下载
long startTime = System.currentTimeMillis();
for (int i = 0; i < THREAD_COUNT; i++) {
final int threadId = i;
final long startPos = i * blockSize;
final long endPos = (i == THREAD_COUNT - 1) ? fileSize - 1 : (i + 1) * blockSize - 1;
pool.execute(() -> {
try {
downloadBlock(FILE_URL, startPos, endPos, threadId);
System.out.println("线程" + threadId + " 下载完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
latch.countDown();
}
});
}
// 5. 等待所有线程完成
latch.await();
pool.shutdown();
// 6. 合并文件
mergeFiles(THREAD_COUNT, OUTPUT_FILE);
long cost = System.currentTimeMillis() - startTime;
System.out.println("下载完成,耗时: " + cost + "ms");
}
/**
* 下载文件的一部分
*/
private static void downloadBlock(String url, long start, long end, int threadId)
throws IOException {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
try (InputStream is = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile(OUTPUT_FILE + ".part" + threadId, "rw")) {
byte[] buffer = new byte[8192];
int len;
while ((len = is.read(buffer)) != -1) {
raf.write(buffer, 0, len);
}
}
}
/**
* 合并分块文件
*/
private static void mergeFiles(int threadCount, String outputFile) throws IOException {
try (OutputStream os = new FileOutputStream(outputFile)) {
for (int i = 0; i < threadCount; i++) {
File partFile = new File(outputFile + ".part" + i);
try (InputStream is = new FileInputStream(partFile)) {
byte[] buffer = new byte[8192];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
}
partFile.delete(); // 删除分块文件
}
}
}
private static long getFileSize(String url) throws IOException {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("HEAD");
return conn.getContentLengthLong();
}
}
17. 常见面试题精选
Q1:创建线程的方式有几种?
4种: 1. 继承Thread类 2. 实现Runnable接口 3. 实现Callable接口(有返回值) 4. 线程池创建(生产环境首选) 本质上只有两种:继承Thread和实现Runnable/Callable 线程池底层也是基于Runnable/Callable
Q2:sleep()和wait()的区别?
1. sleep()是Thread的方法,wait()是Object的方法 2. sleep()不释放锁,wait()释放锁 3. sleep()到时间自动恢复,wait()需要notify()/notifyAll()唤醒 4. sleep()可以在任何地方使用,wait()必须在synchronized块中使用
Q3:synchronized和ReentrantLock的区别?
1. synchronized是关键字,ReentrantLock是类 2. synchronized自动释放锁,ReentrantLock手动unlock(finally中) 3. ReentrantLock支持可中断、超时、公平锁、多条件变量 4. synchronized有锁升级优化 5. 简单场景用synchronized,需要高级特性用ReentrantLock
Q4:线程池的核心参数有哪些?
7个参数: 1. corePoolSize 核心线程数 2. maximumPoolSize 最大线程数 3. keepAliveTime 非核心线程空闲存活时间 4. unit 时间单位 5. workQueue 任务队列 6. threadFactory 线程工厂 7. handler 拒绝策略
Q5:volatile和synchronized的区别?
1. volatile保证可见性和有序性,不保证原子性 synchronized保证原子性、可见性和有序性 2. volatile只能修饰变量,synchronized可以修饰方法和代码块 3. volatile不会阻塞线程,synchronized会阻塞线程 4. volatile更轻量级,性能更好
Q6:CountDownLatch和CyclicBarrier的区别?
1. CountDownLatch计数递减,CyclicBarrier计数递增 2. CountDownLatch不可重用,CyclicBarrier可以reset重用 3. CountDownLatch是一个或多个线程等待其他线程 CyclicBarrier是所有线程互相等待
Q7:ThreadLocal为什么会内存泄漏?
ThreadLocalMap的key是弱引用,value是强引用 当ThreadLocal对象被GC回收后,key变成null 但value仍然被强引用,无法被GC回收 → 内存泄漏 解决方案:使用完毕后调用remove()
18. 总结
知识体系
┌─────────────────────────────────────────────────────────────┐ │ Java多线程知识体系 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 线程基础 │ │ 线程安全 │ │ 线程协作 │ │ │ │ 创建方式 │ │ synchronized│ │ wait/notify│ │ │ │ 生命周期 │ │ volatile │ │ CountDownL.│ │ │ │ 常用方法 │ │ ReentrantL.│ │ CyclicBar. │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 线程池 │ │ 并发容器 │ │ 异步编程 │ │ │ │ ThreadPool │ │ ConcHashMap│ │ CompletableFuture│ │ │ │ 参数/策略 │ │ CopyOnWrite│ │ 链式/组合 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ 原子类 │ │ ThreadLocal│ │ │ │ CAS │ │ 线程隔离 │ │ │ │ ABA问题 │ │ 内存泄漏 │ │ │ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘
最佳实践
1. 生产环境必须使用线程池,不要手动创建线程 2. 线程池使用ThreadPoolExecutor,不要用Executors 3. 线程池使用有界队列,防止OOM 4. 简单同步用synchronized,高级需求用ReentrantLock 5. 读多写少用volatile或CopyOnWriteArrayList 6. 并发Map用ConcurrentHashMap,不要用Hashtable 7. ThreadLocal用完必须remove,防止内存泄漏 8. 异步编程优先使用CompletableFuture 9. 不要在锁内做耗时操作(IO、远程调用) 10. 优先使用并发工具类(BlockingQueue、CountDownLatch等),而不是手动wait/notify
一句话总结
线程是CPU调度的基本单位,线程池管理线程的生命周期,synchronized/volatile/ReentrantLock保证线程安全,CompletableFuture简化异步编程,ThreadLocal实现线程隔离。掌握这些,就掌握了Java并发编程的核心。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)