💡 摘要:你是否曾为程序性能瓶颈而苦恼?是否想充分利用多核CPU的强大能力?是否被线程安全问题和死锁困扰?
别担心,多线程编程是Java中最重要的高级特性之一,它能显著提升程序性能,但同时也带来了复杂的并发问题。
本文将带你从多线程基础概念讲起,理解进程与线程的根本区别。然后深入线程的创建和管理,学习多种线程实现方式。
接着探索线程同步机制,掌握如何解决竞态条件和保证线程安全。最后通过实战案例展示多线程在性能优化、异步处理等场景的应用。从基础概念到高级特性,从内存模型到并发工具,让你全面掌握Java多线程编程。文末附常见问题和面试高频问题,助你写出高性能的并发程序。
一、多线程基础概念
1. 进程 vs 线程
根本区别:
java
// 进程:程序的执行实例,拥有独立的内存空间
// 线程:进程内的执行单元,共享进程内存
public class ProcessVsThread {
public static void main(String[] args) {
// 启动多个进程(不同的Java程序)
// 每个进程有独立的堆、栈、方法区
// 在同一个进程内启动多个线程
Thread thread1 = new Thread(new Task());
Thread thread2 = new Thread(new Task());
thread1.start(); // 共享同一堆内存
thread2.start(); // 共享同一方法区
}
}
class Task implements Runnable {
public void run() {
// 线程执行的任务
}
}
对比表格:
| 特性 | 进程 | 线程 |
| 内存空间 | 独立 | 共享 |
| 通信方式 | 管道、Socket、文件 | 共享变量、队列 |
| 创建开销 | 大 | 小 |
| 上下文切换 | 慢 | 快 |
| 独立性 | 相互隔离 | 相互影响 |
2. 为什么需要多线程?
多线程的优势:
java
public class MultiThreadBenefits {
public static void main(String[] args) {
// 1. 提高CPU利用率(多核处理器)
int processorCores = Runtime.getRuntime().availableProcessors();
System.out.println("可用处理器核心: " + processorCores);
// 2. 提高响应性(GUI应用)
// 主线程处理UI,后台线程处理耗时操作
// 3. 简化建模(模拟现实世界)
// 每个线程可以代表一个独立的活动实体
// 4. 异步处理(非阻塞操作)
// I/O操作时,CPU可以执行其他任务
}
}
二、线程创建与管理
1. 创建线程的三种方式
方式1:继承Thread类
java
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程运行: " + Thread.currentThread().getName());
// 执行具体任务
}
}
// 使用
MyThread thread = new MyThread();
thread.start(); // 启动线程
方式2:实现Runnable接口(推荐)
java
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("任务执行: " + Thread.currentThread().getName());
// 执行具体任务
}
}
// 使用
Thread thread = new Thread(new MyTask());
thread.start();
// 或者使用线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.execute(new MyTask());
方式3:实现Callable接口(带返回值)
java
class ComputeTask implements Callable<Integer> {
private final int number;
public ComputeTask(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
// 模拟耗时计算
Thread.sleep(1000);
return number * number;
}
}
// 使用
ExecutorService executor = Executors.newFixedThreadPool(3);
Future<Integer> future = executor.submit(new ComputeTask(5));
// 获取结果(阻塞直到计算完成)
Integer result = future.get();
System.out.println("计算结果: " + result);
2. 线程生命周期
线程状态转换:
java
public class ThreadStates {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(() -> {
try {
Thread.sleep(2000); // TIMED_WAITING
synchronized (ThreadStates.class) {
ThreadStates.class.wait(); // WAITING
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("新建状态: " + thread.getState()); // NEW
thread.start();
System.out.println("启动后状态: " + thread.getState()); // RUNNABLE
Thread.sleep(100);
System.out.println("运行中状态: " + thread.getState()); // TIMED_WAITING
Thread.sleep(3000);
synchronized (ThreadStates.class) {
ThreadStates.class.notify(); // 唤醒线程
}
Thread.sleep(100);
System.out.println("最终状态: " + thread.getState()); // TERMINATED
}
}
三、线程同步与通信
1. synchronized 关键字
同步方法:
java
class Counter {
private int count = 0;
// 同步方法
public synchronized void increment() {
count++;
}
// 同步静态方法
public static synchronized void staticMethod() {
// 使用Class对象作为锁
}
// 同步代码块
public void incrementWithBlock() {
synchronized (this) { // 使用当前对象作为锁
count++;
}
}
}
2. volatile 关键字
保证可见性:
java
class SharedData {
private volatile boolean running = true;
public void stop() {
running = false; // 修改立即对其他线程可见
}
public void run() {
while (running) {
// 执行任务
}
}
}
3. 锁对象(Lock)
显式锁使用:
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ThreadSafeCounter {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 确保释放锁
}
}
// 尝试获取锁
public boolean tryIncrement() {
if (lock.tryLock()) {
try {
count++;
return true;
} finally {
lock.unlock();
}
}
return false;
}
}
4. 线程通信
wait/notify机制:
java
class MessageQueue {
private String message;
private boolean empty = true;
public synchronized String take() throws InterruptedException {
while (empty) {
wait(); // 释放锁并等待
}
empty = true;
notifyAll(); // 通知生产者
return message;
}
public synchronized void put(String message) throws InterruptedException {
while (!empty) {
wait(); // 释放锁并等待
}
empty = false;
this.message = message;
notifyAll(); // 通知消费者
}
}
四、Java并发工具类
1. CountDownLatch
等待多个任务完成:
java
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
int taskCount = 5;
CountDownLatch latch = new CountDownLatch(taskCount);
for (int i = 0; i < taskCount; i++) {
new Thread(() -> {
try {
// 执行任务
Thread.sleep((long) (Math.random() * 1000));
System.out.println("任务完成: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // 计数减1
}
}).start();
}
latch.await(); // 等待所有任务完成
System.out.println("所有任务已完成,继续主线程");
}
}
2. CyclicBarrier
多线程同步点:
java
public class CyclicBarrierDemo {
public static void main(String[] args) {
int threadCount = 3;
CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
System.out.println("所有线程已到达屏障点");
});
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
System.out.println("线程准备: " + Thread.currentThread().getName());
Thread.sleep((long) (Math.random() * 1000));
barrier.await(); // 等待其他线程
System.out.println("线程继续: " + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
3. Semaphore
控制资源访问:
java
public class SemaphoreDemo {
public static void main(String[] args) {
// 允许3个线程同时访问
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
semaphore.acquire(); // 获取许可
System.out.println("线程开始访问: " + Thread.currentThread().getName());
Thread.sleep(2000); // 模拟资源访问
System.out.println("线程结束访问: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // 释放许可
}
}).start();
}
}
}
五、线程池管理
1. Executor框架
线程池创建:
java
public class ThreadPoolDemo {
public static void main(String[] args) {
// 1. 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(5);
// 2. 缓存线程池(自动扩容)
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 3. 单线程线程池(顺序执行)
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
// 4. 定时任务线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3);
// 提交任务
for (int i = 0; i < 10; i++) {
fixedPool.execute(() -> {
System.out.println("任务执行: " + Thread.currentThread().getName());
});
}
// 优雅关闭
fixedPool.shutdown();
try {
if (!fixedPool.awaitTermination(60, TimeUnit.SECONDS)) {
fixedPool.shutdownNow();
}
} catch (InterruptedException e) {
fixedPool.shutdownNow();
}
}
}
2. 自定义线程池
ThreadPoolExecutor配置:
java
public class CustomThreadPool {
public static void main(String[] args) {
int corePoolSize = 5;
int maxPoolSize = 10;
long keepAliveTime = 60L;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
workQueue,
new ThreadFactory() {
private final AtomicInteger count = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "custom-pool-" + count.getAndIncrement());
}
},
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 使用线程池
for (int i = 0; i < 20; i++) {
executor.execute(new Task(i));
}
executor.shutdown();
}
}
六、线程安全集合
1. ConcurrentHashMap
并发Map实现:
java
public class ConcurrentHashMapDemo {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 多线程安全操作
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
final int index = i;
executor.execute(() -> {
map.put("key" + index, index);
});
}
executor.shutdown();
// 原子操作
map.compute("key1", (k, v) -> v == null ? 1 : v + 1);
map.merge("key2", 1, Integer::sum);
}
}
2. CopyOnWriteArrayList
读多写少场景:
java
public class CopyOnWriteDemo {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// 写操作(复制整个数组)
list.add("item1");
list.add("item2");
// 读操作(无锁,高性能)
for (String item : list) {
System.out.println(item);
}
// 适合监听器列表等读多写少的场景
}
}
七、实战应用案例
1. 生产者-消费者模式
使用BlockingQueue实现:
java
public class ProducerConsumerDemo {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
// 生产者
Runnable producer = () -> {
try {
int value = 0;
while (true) {
queue.put(value);
System.out.println("生产: " + value);
value++;
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
// 消费者
Runnable consumer = () -> {
try {
while (true) {
Integer value = queue.take();
System.out.println("消费: " + value);
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
// 启动生产者和消费者
new Thread(producer).start();
new Thread(consumer).start();
new Thread(consumer).start();
}
}
2. 并行计算
使用ForkJoinPool:
java
public class ParallelCompute {
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
long[] numbers = new long[10000];
// 初始化数组
for (int i = 0; i < numbers.length; i++) {
numbers[i] = i + 1;
}
// 提交任务
Long result = pool.invoke(new SumTask(numbers, 0, numbers.length));
System.out.println("计算结果: " + result);
}
}
class SumTask extends RecursiveTask<Long> {
private final long[] numbers;
private final int start;
private final int end;
private static final int THRESHOLD = 1000;
public SumTask(long[] numbers, int start, int end) {
this.numbers = numbers;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
int length = end - start;
if (length <= THRESHOLD) {
// 直接计算
long sum = 0;
for (int i = start; i < end; i++) {
sum += numbers[i];
}
return sum;
} else {
// 拆分任务
int middle = start + length / 2;
SumTask leftTask = new SumTask(numbers, start, middle);
SumTask rightTask = new SumTask(numbers, middle, end);
leftTask.fork(); // 异步执行左任务
Long rightResult = rightTask.compute(); // 同步执行右任务
Long leftResult = leftTask.join(); // 等待左任务完成
return leftResult + rightResult;
}
}
}
八、常见问题与解决方案
1. 死锁预防
死锁示例与解决:
java
public class DeadlockPrevention {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void method1() {
synchronized (lock1) {
System.out.println("获取lock1");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock2) { // 可能死锁
System.out.println("获取lock2");
}
}
}
public void method2() {
synchronized (lock2) {
System.out.println("获取lock2");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock1) { // 可能死锁
System.out.println("获取lock1");
}
}
}
// 解决方案:按顺序获取锁
public void safeMethod1() {
synchronized (lock1) {
synchronized (lock2) {
// 安全操作
}
}
}
public void safeMethod2() {
synchronized (lock1) {
synchronized (lock2) {
// 安全操作
}
}
}
}
2. 线程池调优
性能优化建议:
java
public class ThreadPoolTuning {
public static void main(String[] args) {
// CPU密集型任务
int cpuCores = Runtime.getRuntime().availableProcessors();
ExecutorService cpuBoundPool = Executors.newFixedThreadPool(cpuCores);
// I/O密集型任务
ExecutorService ioBoundPool = Executors.newCachedThreadPool();
// 混合型任务
ThreadPoolExecutor mixedPool = new ThreadPoolExecutor(
cpuCores * 2, // 核心线程数
cpuCores * 4, // 最大线程数
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000)
);
}
}
九、总结:多线程最佳实践
1. 使用建议
- ✅ 优先使用线程池而不是直接创建线程
- ✅ 使用并发集合而不是同步的集合
- ✅ 尽量使用不可变对象
- ✅ 明确锁的范围,减小同步代码块
- ✅ 使用高级并发工具类
2. 避免的陷阱
- 🔴 避免死锁(按顺序获取锁)
- 🔴 避免过度同步(性能问题)
- 🔴 避免使用stop()、suspend()等过时方法
- 🔴 避免忙等待(使用wait/notify)
十、面试高频问题
❓1. synchronized和ReentrantLock有什么区别?
答:synchronized是关键字,JVM实现;ReentrantLock是类,API更丰富(可中断、超时、公平锁等)。
❓2. volatile关键字有什么作用?
答:保证可见性(修改立即对其他线程可见)和禁止指令重排序,但不保证原子性。
❓3. 什么是线程安全?如何实现?
答:多线程环境下程序的行为符合预期。实现方式:同步、不可变对象、线程局部变量、并发集合等。
❓4. 线程池的核心参数有哪些?
答:核心线程数、最大线程数、空闲时间、工作队列、线程工厂、拒绝策略。
❓5. 如何避免死锁?
答:按固定顺序获取锁、使用尝试获取锁机制、设置超时时间、使用死锁检测工具。