多线程基础
约 1149 字大约 4 分钟
布欧-Lewyon
2026-05-16
在深入线程池和同步机制之前,先打好多线程编程的基础:理解进程与线程的区别、掌握 Thread 类的核心 API,以及了解线程间协作的基本方式。
进程与线程
- 进程:操作系统资源分配的基本单位,拥有独立的内存空间(堆、栈、文件描述符等)。
- 线程:CPU 调度的基本单位,一个进程内的多个线程共享堆内存,但各自拥有独立的栈。
进程 A
├── 线程 A1(栈、程序计数器)
├── 线程 A2(栈、程序计数器)
└── 共享:堆、方法区、文件句柄
进程 B(独立的内存空间,与 A 隔离)多线程在单进程内共享数据,协作效率高,但需要处理同步问题(避免数据竞争)。
线程的创建
继承 Thread
class DownloadThread extends Thread {
private final String url;
DownloadThread(String url) {
this.url = url;
}
@Override
public void run() {
System.out.println("下载: " + url + " 在 " + Thread.currentThread().getName());
}
}
new DownloadThread("https://example.com/file.zip").start();实现 Runnable(推荐)
Runnable task = () -> {
System.out.println("任务执行中: " + Thread.currentThread().getName());
};
Thread thread = new Thread(task, "工作线程-1");
thread.start();Runnable 比继承 Thread 更灵活——类可同时继承其他类。
Thread 核心 API
Thread t = new Thread(() -> {
try {
Thread.sleep(2000); // 休眠 2 秒
} catch (InterruptedException e) {
System.out.println("线程被中断");
}
});
t.setName("worker-1"); // 设置线程名(便于调试)
t.setDaemon(true); // 设为守护线程
t.start();
t.join(1000); // 等待 t 最多 1 秒
System.out.println(t.isAlive()); // 是否仍存活| 方法 | 作用 |
|---|---|
start() | 启动线程(JVM 调用 run()) |
sleep(long millis) | 当前线程休眠(不释放锁) |
join() / join(long millis) | 等待该线程结束 |
interrupt() | 中断线程(设置中断标志) |
isInterrupted() | 检查中断标志 |
setDaemon(boolean) | 设为守护线程,需在 start() 前调用 |
setName(String) | 设置线程名 |
currentThread() | 静态方法,返回当前执行线程 |
守护线程与用户线程
- 用户线程:默认创建的都是用户线程。JVM 等待所有用户线程结束后才退出。
- 守护线程(daemon):为其他线程提供服务的后台线程。所有用户线程退出后,JVM 会立即终止所有守护线程(不执行
finally块)。
Thread daemon = new Thread(() -> {
while (true) {
// 后台清理任务
}
});
daemon.setDaemon(true);
daemon.start();典型守护线程:GC(垃圾回收器)、监控告警线程。
线程中断
Java 的中断是协作式的——一个线程向另一个线程发送"请停止"的信号,由被中断线程自行决定如何响应:
Thread worker = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 正常工作
}
System.out.println("收到中断信号,停止工作");
});
worker.start();
Thread.sleep(2000);
worker.interrupt(); // 发送中断信号Thread.sleep()、wait()、join()在阻塞状态下收到中断时会抛出InterruptedException并清除中断标志。- 捕获
InterruptedException后,通常应恢复中断标志:Thread.currentThread().interrupt()。
wait / notify:线程间协作
wait() 和 notify() 是 Object 类的方法,用于线程间发送信号。它们必须在 synchronized 代码块中调用:
public class MessageQueue {
private String message;
private boolean empty = true;
public synchronized void put(String msg) throws InterruptedException {
while (!empty) {
wait(); // 队列非空时等待消费者取出
}
message = msg;
empty = false;
notifyAll(); // 唤醒所有等待的消费者
}
public synchronized String take() throws InterruptedException {
while (empty) {
wait(); // 队列为空时等待生产者放入
}
empty = true;
notifyAll(); // 唤醒所有等待的生产者
return message;
}
}wait()释放锁并进入 WAITING 状态,直到其他线程调用notify()/notifyAll()。- 始终在循环中检查等待条件(防止虚假唤醒)。
notify()唤醒一个等待线程,notifyAll()唤醒所有——大多数场景推荐notifyAll()。
小结
- 线程是进程内的执行单元,共享堆内存但独立栈;多线程编程的核心挑战是数据竞争和协作。
- 创建线程优先用
Runnable而非继承Thread。 sleep(休眠)、join(等待结束)、interrupt(协作中断)是 Thread 的三大基础操作。- 守护线程在所有用户线程退出后自动终止,适合后台服务类任务。
wait/notify实现线程间协作,必须在synchronized块中使用。- 易错:
thread.interrupt()不会强制终止线程,只是设置标志位。sleep()中收到中断会抛InterruptedException并清空中断标志——需重新设置中断状态。此外,setDaemon(true)必须在start()之前调用,否则抛IllegalThreadStateException。 - 思考任务:写一个生产者—消费者程序:一个线程每秒生产一个数字(1~10),另一个线程消费并打印平方值,用
wait/notify实现同步。
上一节:JDBC 基础
下一节:线程与线程池
