并发编程基础
00:00
线程、锁与并发工具
概述
Java 并发编程是多线程环境下编写正确程序的基础。本文从线程创建、同步机制到线程间通信,系统介绍 Java 并发编程的核心概念。理解线程安全、锁机制和内存模型是编写高质量并发代码的前提。
基础概念
线程状态
| 状态 | 说明 |
|---|---|
| NEW | 线程已创建但未启动 |
| RUNNABLE | 可运行状态(包括运行中和就绪) |
| BLOCKED | 阻塞于锁 |
| WAITING | 无限期等待(如 Object.wait) |
| TIMED_WAITING | 有限期等待(如 Thread.sleep) |
| TERMINATED | 线程执行完毕 |
线程安全等级
| 等级 | 说明 | 示例 |
|---|---|---|
| 不可变 | 对象创建后状态不可变 | String, Integer |
| 绝对安全 | 任何环境下都线程安全 | AtomicInteger |
| 相对安全 | 单个操作安全,复合操作需额外同步 | Vector, HashTable |
| 线程不安全 | 多线程下需外部同步 | ArrayList, HashMap |
Java 内存模型(JMM)
- 可见性:一个线程修改共享变量后,其他线程能立即看到修改
- 原子性:操作不可被中断,要么全部执行要么不执行
- 有序性:程序执行顺序符合预期,编译器和处理器不会随意重排
快速上手
线程创建
// 方式一:继承 Thread 类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程运行: " + getName());
}
}
new MyThread().start();
// 方式二:实现 Runnable 接口(推荐)
Thread t = new Thread(() -> {
System.out.println("线程运行: " + Thread.currentThread().getName());
}, "my-thread");
t.start();
// 方式三:Callable + FutureTask(有返回值)
FutureTask<String> task = new FutureTask<>(() -> {
Thread.sleep(1000);
return "计算结果";
});
new Thread(task).start();
String result = task.get(); // 阻塞等待结果
// 方式四:虚拟线程(Java 21+)
Thread vt = Thread.startVirtualThread(() -> {
System.out.println("虚拟线程运行");
});
synchronized 同步
// 同步代码块:指定锁对象
synchronized (lock) {
// 临界区:同一时刻只有一个线程能进入
balance += amount;
}
// 同步实例方法:锁为 this
public synchronized void withdraw(int amount) {
if (balance >= amount) {
balance -= amount;
}
}
// 同步静态方法:锁为 Class 对象
public synchronized static int getInstanceCount() {
return count;
}
volatile 关键字
// volatile 保证可见性和有序性,但不保证原子性
public class StatusFlag {
private volatile boolean running = true;
public void stop() {
running = false; // 其他线程立即可见
}
public void work() {
while (running) { // 每次读取最新值
doWork();
}
}
}
// 注意:volatile 不能保证复合操作的原子性
private volatile int counter = 0;
counter++; // 不安全!包含读取、加1、写入三个操作
详细用法
ReentrantLock
// ReentrantLock 比 synchronized 更灵活
private final ReentrantLock lock = new ReentrantLock();
public void transfer(Account from, Account to, int amount) {
lock.lock();
try {
from.debit(amount);
to.credit(amount);
} finally {
lock.unlock(); // 必须在 finally 中释放
}
}
// 可中断锁:响应中断
lock.lockInterruptibly();
// 定时锁:避免死锁
if (lock.tryLock(5, TimeUnit.SECONDS)) {
try { doWork(); }
finally { lock.unlock(); }
} else {
// 获取锁超时,执行备选逻辑
}
// 公平锁:按等待顺序获取
ReentrantLock fairLock = new ReentrantLock(true);
线程间通信
// wait/notify 机制
class BoundedBuffer {
private final Queue<String> queue = new LinkedList<>();
private final int capacity = 10;
// 生产者
public synchronized void put(String item) throws InterruptedException {
while (queue.size() == capacity) {
wait(); // 队列满时等待
}
queue.add(item);
notifyAll(); // 通知消费者
}
// 消费者
public synchronized String take() throws InterruptedException {
while (queue.isEmpty()) {
wait(); // 队列空时等待
}
String item = queue.poll();
notifyAll(); // 通知生产者
return item;
}
}
线程池基础
// 使用 ExecutorService 管理线程
ExecutorService executor = Executors.newFixedThreadPool(4);
// 提交任务
Future<String> future = executor.submit(() -> {
return fetchData();
});
// 获取结果
try {
String result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
future.cancel(true); // 超时取消
}
// 关闭线程池
executor.shutdown();
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 强制关闭
}
常见场景
单例模式(线程安全)
// 双重检查锁定(DCL)
public class Singleton {
private static volatile Singleton instance; // volatile 防止指令重排
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查(无锁)
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查(有锁)
instance = new Singleton();
}
}
}
return instance;
}
}
// 更简洁的方式:使用枚举
public enum SingletonEnum {
INSTANCE;
public void doSomething() { }
}
生产者-消费者模式
// 使用 BlockingQueue 实现生产者-消费者
BlockingQueue<String> queue = new ArrayBlockingQueue<>(100);
// 生产者线程
Thread producer = new Thread(() -> {
try {
while (true) {
String data = fetchData();
queue.put(data); // 队列满时自动阻塞
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 消费者线程
Thread consumer = new Thread(() -> {
try {
while (true) {
String data = queue.take(); // 队列空时自动阻塞
processData(data);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
注意事项
- wait() 必须在 synchronized 块中调用,否则抛出 IllegalMonitorStateException
- wait/notify 应该使用 while 循环检查条件,不要使用 if(防止虚假唤醒)
- synchronized 和 ReentrantLock 不要混用,选择一种即可
- 线程池关闭时应先 shutdown 再 awaitTermination,最后 shutdownNow
- 避免在持有锁时调用外部方法,可能导致死锁
- 线程中断应通过 interrupt 机制协作,不要使用已废弃的 stop/suspend
进阶用法
读写锁
// 读多写少场景使用 ReadWriteLock
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Map<String, User> cache = new HashMap<>();
// 读操作:多个线程可同时读
public User get(String key) {
rwLock.readLock().lock();
try {
return cache.get(key);
} finally {
rwLock.readLock().unlock();
}
}
// 写操作:独占锁
public void put(String key, User user) {
rwLock.writeLock().lock();
try {
cache.put(key, user);
} finally {
rwLock.writeLock().unlock();
}
}
ThreadLocal 线程本地变量
// ThreadLocal 为每个线程维护独立的变量副本
private static final ThreadLocal<SimpleDateFormat> dateFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
// 使用
String formatted = dateFormat.get().format(new Date());
// 线程池中使用 ThreadLocal 必须清理
executor.submit(() -> {
try {
dateFormat.get().format(date);
} finally {
dateFormat.remove(); // 防止内存泄漏
}
});