概述
在Java中,多线程是指在一个程序中每个并发执行多个线程的技术。线程是一个独立的执行路径,程序通过多线程来出现并发任务,从而提高效率,尤其是在处理I/O密集型或者计算密集型任务时。
一、多线程核心概念
1. 线程创建方式
(1) 继承 Thread
类(不推荐)
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
}
// 使用
new MyThread().start();
(2) 实现 Runnable
接口(推荐)
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable running: " + Thread.currentThread().getName());
}
}
// 使用
new Thread(new MyRunnable()).start();
(3) 实现 Callable
+ Future
(获取返回值)
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
TimeUnit.SECONDS.sleep(2);
return 42;
});
System.out.println("Result: " + future.get()); // 阻塞获取结果
executor.shutdown();
2. 线程池(实际开发必用)
推荐使用 ThreadPoolExecutor
自定义线程池:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲线程存活时间
new ArrayBlockingQueue<>(100), // 任务队列
new ThreadFactoryBuilder().setNameFormat("worker-%d").build(), // 线程命名
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
拒绝策略选择:
-
AbortPolicy
(默认):抛出异常 -
CallerRunsPolicy
:由调用线程执行任务 -
DiscardOldestPolicy
:丢弃最旧任务 -
DiscardPolicy
:静默丢弃
二、实际应用场景
场景 1:批量数据处理(电商订单处理)
List<Order> orders = getBatchOrders(1000); // 获取1000个待处理订单
// 分片处理:每100个订单为一组
List<List<Order>> chunks = Lists.partition(orders, 100);
ExecutorService executor = Executors.newFixedThreadPool(10); // 10线程并发处理
List<Future<Boolean>> futures = new ArrayList<>();
for (List<Order> chunk : chunks) {
futures.add(executor.submit(() -> {
processOrders(chunk); // 处理订单组
return true;
}));
}
// 等待所有任务完成
for (Future<Boolean> future : futures) {
future.get(); // 阻塞等待
}
executor.shutdown();
场景 2:异步日志记录(避免阻塞主流程)
// 使用单线程池处理日志(保证顺序)
private static final ExecutorService LOG_EXECUTOR =
Executors.newSingleThreadExecutor(r -> new Thread(r, "log-thread"));
public void saveOrder(Order order) {
// 主流程快速返回
LOG_EXECUTOR.submit(() -> {
try {
logService.saveLog("Order created: " + order.getId());
} catch (Exception e) {
// 必须捕获异常,否则会静默失败!
logger.error("Log save failed", e);
}
});
}
场景 3:定时任务调度(使用 ScheduledExecutorService
)
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 每天凌晨执行数据统计
scheduler.scheduleAtFixedRate(() -> {
generateDailyReport();
}, initialDelay, 24 * 60 * 60, TimeUnit.SECONDS);
// 每5分钟检查一次系统状态
scheduler.scheduleWithFixedDelay(() -> {
checkSystemHealth();
}, 0, 5, TimeUnit.MINUTES);
三、线程安全与同步
1. 线程安全容器
// 代替 HashMap
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// 代替 ArrayList
List<String> copyOnWriteList = new CopyOnWriteArrayList<>();
// 高并发计数器
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // 原子操作
2. 显式锁(更灵活的控制)
private final ReentrantLock lock = new ReentrantLock();
private int sharedResource = 0;
public void updateResource() {
lock.lock();
try {
sharedResource++;
// 其他操作...
} finally {
lock.unlock(); // 必须放在finally块中!
}
}
3. 读写锁(读多写少场景)
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
public String readData() {
readLock.lock();
try {
return data; // 并发读不互斥
} finally {
readLock.unlock();
}
}
public void writeData(String newData) {
writeLock.lock();
try {
data = newData; // 写操作独占
} finally {
writeLock.unlock();
}
}
四、避坑指南(血泪经验)
1. 避免死锁
-
按固定顺序获取锁
-
使用
tryLock()
设置超时
if (lock1.tryLock(1, TimeUnit.SECONDS)) {
try {
if (lock2.tryLock(1, TimeUnit.SECONDS)) {
try {
// 操作资源
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
2. 防止资源泄漏
-
必须关闭线程池!
executor.shutdown();
// 或强制终止
List<Runnable> unfinished = executor.shutdownNow();
3. 异常处理
-
线程内部异常不会传递到外层
ExecutorService executor = Executors.newFixedThreadPool(1);
executor.submit(() -> {
try {
riskyOperation();
} catch (Exception e) {
logger.error("Task failed", e); // 必须内部捕获
}
});
4. 性能陷阱
-
避免过度同步(缩小同步代码块)
-
警惕上下文切换开销(线程数不是越多越好)
-
使用
ThreadLocal
谨慎(可能导致内存泄漏)
五、高级特性(Java 8+)
1. CompletableFuture(异步编排)
CompletableFuture.supplyAsync(() -> fetchUserInfo(userId), executor)
.thenApplyAsync(user -> calculateScore(user), executor)
.thenAcceptAsync(score -> saveToDB(score), executor)
.exceptionally(ex -> {
logger.error("Pipeline failed", ex);
return null;
});
2. 并行流(谨慎使用)
List<Data> results = dataList.parallelStream()
.filter(d -> d.getValue() > 100)
.collect(Collectors.toList());
六、调试与监控
1. 线程堆栈分析
# 生成线程转储
jstack <pid> > thread_dump.txt
# 查找死锁(输出中搜索 "deadlock")
2. 监控工具
-
VisualVM:查看线程状态
-
Arthas:在线诊断生产环境线程问题
-
Prometheus + Grafana:监控线程池指标
七、最佳实践总结
-
尽量使用线程池,避免直接
new Thread
-
优先使用并发容器(如
ConcurrentHashMap
) -
同步代码块尽量小
-
始终处理线程中的异常
-
生产环境必须命名线程(方便排查问题)
-
合理设置线程池参数(通过压测确定)
-
避免依赖线程优先级(不同平台行为不一致)
通过合理运用多线程技术,可以显著提升系统吞吐量,但需时刻警惕其复杂性。建议在关键模块编写详细的并发处理文档,并进行充分的压力测试。