Appearance
🔄 多线程基础
多线程概述
多线程(Multithreading)允许程序同时执行多个任务,提高程序的并发性和响应性。
线程与进程
- 进程:程序的一次执行,有独立的地址空间
- 线程:进程内的执行单元,共享进程的地址空间
创建线程
方式1:继承 Thread 类
java
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("线程运行: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 使用
MyThread thread = new MyThread();
thread.start();方式2:实现 Runnable 接口
java
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("线程运行: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 使用
Thread thread = new Thread(new MyRunnable());
thread.start();方式3:Lambda 表达式(Java 8+)
java
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("线程运行: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();线程状态
线程状态转换
新建 → 就绪 → 运行 → 阻塞 → 就绪 → 运行 → 终止线程状态方法
java
Thread thread = new Thread(() -> {
// 线程代码
});
// 获取状态
Thread.State state = thread.getState();
System.out.println("线程状态: " + state);
// 启动线程
thread.start();
// 等待线程完成
thread.join();
// 中断线程
thread.interrupt();
// 检查是否中断
boolean interrupted = thread.isInterrupted();线程同步
synchronized 关键字
java
public class Counter {
private int count = 0;
// 同步方法
public synchronized void increment() {
count++;
}
// 同步代码块
public void increment2() {
synchronized (this) {
count++;
}
}
}wait 和 notify
java
public class ProducerConsumer {
private int value;
private boolean hasValue = false;
public synchronized void produce(int val) {
while (hasValue) {
try {
wait(); // 等待消费者消费
} catch (InterruptedException e) {
e.printStackTrace();
}
}
value = val;
hasValue = true;
notify(); // 通知消费者
}
public synchronized int consume() {
while (!hasValue) {
try {
wait(); // 等待生产者生产
} catch (InterruptedException e) {
e.printStackTrace();
}
}
hasValue = false;
notify(); // 通知生产者
return value;
}
}可见性、有序性与原子性(JMM 速览)
- 可见性:一个线程对共享变量的写入何时对其他线程可见(
volatile/锁)。 - 有序性:编译器与 CPU 允许重排序,但需满足
happens-before规则。 - 原子性:不可再分的操作,
long/double在早期 32 位上非原子(现代实现已保证)。
volatile 的正确用法
java
class StopSignal {
private volatile boolean stopped = false;
public void stop() { stopped = true; }
public void run() {
while (!stopped) {
// do work
}
}
}- 保障可见性与禁止特定场景下的重排序;不保证复合操作的原子性。
指令重排与 DCL 单例
java
public class Singleton {
private static volatile Singleton INSTANCE;
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) INSTANCE = new Singleton();
}
}
return INSTANCE;
}
}锁与并发原语
synchronized 的对象锁与类锁
- 实例方法锁的是
this,静态方法锁的是Class对象。 - 尽量缩小同步代码块,避免无谓竞争。
ReentrantLock 与条件队列
java
Lock lock = new ReentrantLock();
Condition notEmpty = lock.newCondition();
Condition notFull = lock.newCondition();- 支持可中断、可定时、公平/非公平、多个条件队列。
读写锁与 StampedLock(简介)
ReentrantReadWriteLock:读多写少的场景有效。StampedLock:支持乐观读,需注意中断与可重入限制。
线程间协作模式
生产者-消费者(BlockingQueue)
java
BlockingQueue<Integer> q = new ArrayBlockingQueue<>(100);
// 生产者:q.put(x); 消费者:q.take();Future/CompletableFuture 概览
java
CompletableFuture<Integer> f = CompletableFuture.supplyAsync(() -> 42)
.thenApply(x -> x + 1)
.exceptionally(ex -> -1);线程池实践
为什么优先使用线程池
- 复用线程降低创建销毁成本;可控的并发度;可观测与监控。
正确创建线程池(避免 Executors 工厂默认陷阱)
java
ThreadPoolExecutor pool = new ThreadPoolExecutor(
8, 16,
60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000),
new ThreadFactoryBuilder().setNameFormat("biz-pool-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy()
);- 自定义队列与拒绝策略;命名线程便于定位;关注饱和时行为。
线程池参数调优指引
- 计算密集:核心线程≈CPU 核心数或略多;IO 密集:核心线程数×(1+平均阻塞系数)。
- 使用监控(JFR/Arthas/日志)观察队列长度、活跃线程与任务耗时。
常见并发 Bug 与定位
- 死锁:循环等待 + 互斥 + 不可剥夺 + 占有且等待。
java
// 使用 jstack <pid> 查看 FOUND ONE JAVA-LEVEL DEADLOCK- 活锁:线程不断重试互相谦让但无进展。
- 饥饿:低优先级线程长期得不到调度(不建议修改线程优先级)。
ThreadLocal 与内存泄漏
- 在线程池复用下,
ThreadLocal值需及时清理:try { set } finally { remove }。 - 避免在大对象/连接上长期持有引用。
False Sharing(伪共享)
- 多线程写入相邻内存导致同一缓存行反复失效。
- 通过
@Contended(需-XX:-RestrictContended)或手动填充避免。
练习与思考
- 用
BlockingQueue实现多生产者-多消费者,统计吞吐与延迟。 - 将
synchronized改为ReentrantLock,在超时与可中断场景比较表现。 - 使用
CompletableFuture改造串行调用为并行聚合。
完整示例
java
public class ThreadExample {
public static void main(String[] args) {
// 创建线程
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("线程1: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("线程2: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 启动线程
thread1.start();
thread2.start();
// 等待线程完成
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有线程完成");
}
}下一步
掌握了多线程基础后,可以继续学习:
💡 提示:多线程编程可以提高程序性能,但需要注意线程安全问题
