Java 并发编程
Java 并发编程
Java 并发编程的核心问题是:多个线程同时访问共享资源时,如何保证数据正确性。关键概念包括锁、内存可见性、线程协作。
线程生命周期
线程在六种状态间流转,状态迁移由"锁、等待、阻塞"三类操作驱动:
| 状态 | 含义 | 进入方式 | 离开方式 |
|---|---|---|---|
| NEW(新建) | 已创建未启动 | new Thread() | start() |
| RUNNABLE(可运行) | 就绪或运行中 | start() 或抢占到 CPU | 时间片用完/让出 |
| BLOCKED(阻塞) | 等待获取锁 | 进入 synchronized 块但锁被占用 | 获取到锁 |
| WAITING(等待) | 无限等待其他线程 | wait() / join() / park() | notify() / notifyAll() / 目标线程结束 |
| TIMED_WAITING(限期等待) | 有时间限制的等待 | sleep(ms) / wait(ms) / join(ms) | 超时/被唤醒 |
| TERMINATED(终止) | 执行完或异常退出 | run() 返回 | — |
区分 BLOCKED 与 WAITING
BLOCKED 是"等锁"(synchronized 排队),WAITING 是"等信号"(主动让出)。看线程 dump(jstack)时,大量 BLOCKED 说明锁竞争激烈,大量 WAITING 说明线程在空等条件——前者查锁竞争,后者查 notify 是否漏发。
wait() 和 sleep() 的区别
| 方法 | 所属 | 是否释放锁 | 唤醒方式 |
|---|---|---|---|
wait() | Object | 是,释放对象锁 | notify() / notifyAll() |
sleep() | Thread | 否,不释放锁 | 超时 / 中断 |
// wait 需要在 synchronized 块中调用
synchronized (mon) {
mon.wait(); // 释放锁,进入等待状态
}
// sleep 不释放锁
synchronized(LOCK) {
Thread.sleep(1000); // LOCK 仍然被持有
}注意
伪唤醒防护 wait() 可能发生伪唤醒(spurious wakeup),必须用循环检查条件:
while (!condition) { mon.wait(); }ThreadLocal
为每个线程提供独立的对象实例,避免线程安全问题。
private static final ThreadLocal<SimpleDateFormat> formatter =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd HHmm"));内存泄漏风险
线程池中线程是复用的,不调用 remove() 清理会导致引用一直存在。使用完必须 threadLocal.remove()。
ThreadLocal 工作原理:
Runnable vs Thread
实现 Runnable 优于继承 Thread——灵活且不占用继承位。Runnable 是任务抽象,Thread 是线程载体。
// 推荐
new Thread(new MyTask()).start();阻塞队列
BlockingQueue 是 JUC 包提供的线程安全队列,支持阻塞式存取,常用于生产者-消费者模式。
| 状态 | 行为 |
|---|---|
| 队列为空 | 消费者阻塞,等待非空 |
| 队列已满 | 生产者阻塞,等待非满 |
生产者-消费者模式:
| 实现 | 类型 | 说明 |
|---|---|---|
| ArrayBlockingQueue | 有界 | 基于数组,需指定容量 |
| LinkedBlockingQueue | 可选有界 | 基于链表,默认无界 |
| DelayQueue | 无界 | 延迟获取元素 |
| SynchronousQueue | 不存储 | 直接传递,一对一交接 |
| 方法 | 队列满/空时 | 返回值 |
|---|---|---|
put(e) | 阻塞等待 | void |
take() | 阻塞等待 | E |
offer(e) | 返回 false | boolean |
poll() | 返回 null | E |
DelayQueue 示例:
public class DelayedTask implements Delayed {
private long executeTime;
private Runnable task;
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(executeTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return Long.compare(this.executeTime, ((DelayedTask) o).executeTime);
}
}并发基石
同步、线程池、并发容器是并发编程的三个支柱,各自有专门笔记:
| 主题 | 笔记 | 覆盖内容 |
|---|---|---|
| 锁与同步 | Java锁机制 | synchronized、ReentrantLock、CAS、AQS、并发容器 |
| 线程池 | Java线程池 | ThreadPoolExecutor 七参数、拒绝策略、参数配置 |
| 内存模型 | JVM | JMM、volatile、Happens-Before、锁升级 |
本文只覆盖并发基础概念(wait/sleep、ThreadLocal、Runnable、阻塞队列),更深层的锁原理、无锁并发、线程池配置分别见上述笔记——它们共同构成 Java 并发知识全貌。