一、多线程基础概念
1. 什么是线程?
线程是程序中执行的最小单位,一个进程可以包含多个线程。例如,浏览器可以同时下载文件、播放视频和渲染页面,这些都是通过不同线程实现的。
2. 为什么需要多线程?
- 提高 CPU 利用率:当一个线程等待 IO 操作时,其他线程可以继续执行。
- 改善响应性:例如 GUI 应用中,主线程负责界面响应,后台线程处理耗时任务。
- 充分利用多核 CPU:现代处理器通常有多核,多线程可以并行执行在不同核心上。
二、Java 中创建线程的方式
1. 继承 Thread 类
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行中: " + Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程,JVM会调用run()方法
System.out.println("主线程: " + Thread.currentThread().getName());
}
}
2. 实现 Runnable 接口
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程执行中: " + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
System.out.println("主线程: " + Thread.currentThread().getName());
}
}
3. 实现 Callable 接口(带返回值)
import java.util.concurrent.*;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "任务执行完成: " + Thread.currentThread().getName();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new MyCallable());
System.out.println(future.get()); // 获取返回值,会阻塞直到任务完成
executor.shutdown();
}
}
三、线程的生命周期
Java 线程有 6 种状态,定义在Thread.State
枚举中:
- NEW:线程刚被创建,但还未调用
start()
方法。 - RUNNABLE:线程正在 Java 虚拟机中执行,或者准备执行(等待 CPU 时间片)。
- BLOCKED:线程被阻塞,等待获取监视器锁(例如进入
synchronized
块 / 方法)。 - WAITING:线程无限期等待另一个线程执行特定操作(例如调用
wait()
、join()
)。 - TIMED_WAITING:线程在指定时间内等待(例如调用
Thread.sleep(1000)
)。 - TERMINATED:线程执行完毕或因异常终止。
四、线程同步与锁
1. synchronized 关键字
用于实现线程同步,确保同一时间只有一个线程可以执行被保护的代码块或方法。
public class Counter {
private int count = 0;
// 同步方法
public synchronized void increment() {
count++;
}
// 同步代码块
public void decrement() {
synchronized (this) {
count--;
}
}
}
2. ReentrantLock
java.util.concurrent.locks
包中的显式锁,功能比synchronized
更强大。
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock(); // 必须在finally中释放锁
}
}
}
3. 原子操作类
java.util.concurrent.atomic
包提供了原子操作类,基于 CAS(Compare-and-Swap)实现无锁并发。
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // 原子操作
}
}
五、线程间通信
1. wait()、notify()、notifyAll()
这些方法是 Object 类的一部分,用于线程间协作。
public class ProducerConsumer {
private static final Object lock = new Object();
private static boolean ready = false;
public static void main(String[] args) {
// 生产者线程
new Thread(() -> {
synchronized (lock) {
System.out.println("生产者准备数据...");
ready = true;
lock.notify(); // 通知等待的线程
}
}).start();
// 消费者线程
new Thread(() -> {
synchronized (lock) {
while (!ready) {
try {
lock.wait(); // 等待数据准备好
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("消费者处理数据");
}
}).start();
}
}
2. Condition 接口
ReentrantLock
的高级替代方案,提供更灵活的等待 / 通知机制。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumer {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private boolean ready = false;
public void produce() {
lock.lock();
try {
System.out.println("生产者准备数据...");
ready = true;
condition.signal(); // 唤醒等待的线程
} finally {
lock.unlock();
}
}
public void consume() {
lock.lock();
try {
while (!ready) {
condition.await(); // 等待数据准备好
}
System.out.println("消费者处理数据");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
六、高级主题
1. 线程池(Executor 框架)
线程池管理一组工作线程,避免频繁创建和销毁线程的开销。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任务
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("执行任务: " + taskId + " 线程: " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 关闭线程池
executor.shutdown();
}
}
2. 并发集合类
java.util.concurrent
包提供了多种线程安全的集合类:
- ConcurrentHashMap:高效的线程安全哈希表
- CopyOnWriteArrayList:写时复制的列表,适合读多写少的场景
- BlockingQueue:阻塞队列,常用于生产者 - 消费者模式
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class BlockingQueueExample {
public static void main(String[] args) {
BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);
// 生产者线程
new Thread(() -> {
try {
queue.put("数据1");
queue.put("数据2");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 消费者线程
new Thread(() -> {
try {
System.out.println("消费: " + queue.take());
System.out.println("消费: " + queue.take());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
3. Future 和 CompletableFuture
用于异步计算和处理结果。
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "异步任务结果";
});
// 任务完成后执行回调
future.thenAccept(result -> System.out.println("处理结果: " + result));
// 阻塞等待结果
System.out.println(future.get());
}
}
4. 原子变量与 CAS
AtomicInteger
、AtomicLong
等原子类基于 CAS 实现无锁并发。
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
private static AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.incrementAndGet();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.incrementAndGet();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("计数器值: " + counter.get()); // 输出2000,无竞争问题
}
}
七、线程安全实践建议
- 不可变对象:优先使用不可变对象(如
String
、Integer
),它们天生线程安全。 - 最小化共享状态:尽量减少多线程共享的变量和资源。
- 使用线程安全类:优先使用
java.util.concurrent
包中的类,而非自己实现同步。 - 避免死锁:
- 按固定顺序获取锁
- 使用带超时的锁获取方法(如
tryLock
)
- 正确处理中断:在线程中正确响应
interrupt()
请求,避免资源泄漏。
八、性能调优与监控
-
线程池大小优化:
- CPU 密集型任务:线程数 = CPU 核心数 + 1
- IO 密集型任务:线程数 = CPU 核心数 × (1 + 平均等待时间 / 平均处理时间)
-
工具与监控:
- jstack:打印线程堆栈信息,用于分析死锁和阻塞问题
- VisualVM:可视化监控工具,查看线程状态、CPU 使用率等
- ThreadMXBean:Java 代码中获取线程信息的 API
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
public class ThreadMonitor {
public static void main(String[] args) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadMXBean.getAllThreadIds();
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadIds, 10);
for (ThreadInfo info : threadInfos) {
System.out.println("线程: " + info.getThreadName() + " 状态: " + info.getThreadState());
}
}
}