1. 项目概述
作为一名在Java并发领域摸爬滚打多年的老码农,我经常看到新手在面对多线程编程时手足无措的样子。今天我们就来聊聊Java多线程设计模式的实战应用,通过单例、阻塞队列、定时器和线程池这四个经典场景,带你打通多线程编程的任督二脉。
多线程设计模式不是纸上谈兵的理论,而是经过无数项目验证的最佳实践方案。就拿电商系统来说,秒杀活动需要单例模式保证库存计数的一致性,订单处理需要阻塞队列实现生产者-消费者模型,定时任务需要精确的定时器调度,而线程池则是整个系统稳定运行的基石。掌握这些模式,你就能写出既高效又安全的并发代码。
2. 单例模式:线程安全的全局访问点
2.1 为什么需要单例模式
想象一下,你正在开发一个配置管理中心,系统中所有模块都需要读取相同的配置信息。如果每次请求都创建一个新的配置对象,不仅浪费内存,更可能导致配置不一致。这时就需要单例模式来确保全局唯一实例。
但在多线程环境下,简单的单例实现可能会创建多个实例。我曾在项目中遇到过这样的bug:两个线程同时检测到实例为空,结果都执行了new操作,导致系统出现两个配置管理器,引发了一系列诡异的问题。
2.2 五种单例实现方式对比
- 饿汉式:类加载时就初始化实例
java复制public class ConfigManager {
private static final ConfigManager instance = new ConfigManager();
private ConfigManager() {}
public static ConfigManager getInstance() {
return instance;
}
}
优点:实现简单,线程安全
缺点:可能造成资源浪费(如果实例一直未被使用)
- 懒汉式(非线程安全版):首次调用时创建实例
java复制public class ConfigManager {
private static ConfigManager instance;
private ConfigManager() {}
public static ConfigManager getInstance() {
if (instance == null) {
instance = new ConfigManager();
}
return instance;
}
}
注意:这个版本在多线程环境下不安全!
- 懒汉式(同步方法版):
java复制public synchronized static ConfigManager getInstance() {
if (instance == null) {
instance = new ConfigManager();
}
return instance;
}
优点:线程安全
缺点:每次获取实例都要同步,性能差
- 双重检查锁定(DCL):
java复制public class ConfigManager {
private volatile static ConfigManager instance;
private ConfigManager() {}
public static ConfigManager getInstance() {
if (instance == null) {
synchronized (ConfigManager.class) {
if (instance == null) {
instance = new ConfigManager();
}
}
}
return instance;
}
}
关键点:必须使用volatile防止指令重排序
这是性能与安全兼顾的最佳方案
- 静态内部类:
java复制public class ConfigManager {
private ConfigManager() {}
private static class Holder {
private static final ConfigManager INSTANCE = new ConfigManager();
}
public static ConfigManager getInstance() {
return Holder.INSTANCE;
}
}
优点:懒加载+线程安全+无同步开销
个人最推荐这种方式
2.3 单例模式实战技巧
- 如果单例需要实现Serializable接口,必须同时实现readResolve()方法防止反序列化创建新实例:
java复制private Object readResolve() {
return getInstance();
}
-
在Spring等框架中,通常使用容器管理的单例(@Scope("singleton"))而非手动实现
-
单例对象的初始化要特别注意异常处理,一旦初始化失败,整个程序生命周期都无法再获取实例
3. 阻塞队列:生产者-消费者模式的核心
3.1 阻塞队列的应用场景
去年我参与开发了一个日志收集系统,需要处理来自上千个客户端的日志数据。使用阻塞队列作为缓冲区,完美解决了生产者和消费者速度不匹配的问题。当队列满时,生产者线程自动阻塞;当队列空时,消费者线程自动阻塞,无需手动管理线程等待和唤醒。
3.2 Java中的阻塞队列实现
Java并发包提供了多种阻塞队列实现:
- ArrayBlockingQueue:基于数组的有界队列
java复制BlockingQueue<String> queue = new ArrayBlockingQueue<>(1000);
- LinkedBlockingQueue:基于链表的可选有界队列
java复制// 无界队列
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
// 有界队列
BlockingQueue<String> queue = new LinkedBlockingQueue<>(1000);
- PriorityBlockingQueue:支持优先级的无界队列
java复制BlockingQueue<LogEvent> queue = new PriorityBlockingQueue<>(11,
(a, b) -> a.getPriority() - b.getPriority());
- SynchronousQueue:不存储元素的特殊队列
java复制// 每个插入操作必须等待另一个线程的移除操作
BlockingQueue<String> queue = new SynchronousQueue<>();
3.3 生产者-消费者模式实现
来看一个完整的日志处理案例:
java复制// 日志生产者
class LogProducer implements Runnable {
private final BlockingQueue<String> queue;
public LogProducer(BlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
while (true) {
String log = generateLog();
queue.put(log); // 队列满时会自动阻塞
System.out.println("Produced: " + log);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private String generateLog() {
return "Log-" + System.currentTimeMillis();
}
}
// 日志消费者
class LogConsumer implements Runnable {
private final BlockingQueue<String> queue;
public LogConsumer(BlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
while (true) {
String log = queue.take(); // 队列空时会自动阻塞
processLog(log);
System.out.println("Consumed: " + log);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void processLog(String log) {
// 模拟日志处理耗时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
// 启动生产者和消费者
public class LogSystem {
public static void main(String[] args) {
BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(new LogProducer(queue));
executor.execute(new LogConsumer(queue));
executor.shutdown();
}
}
3.4 阻塞队列使用注意事项
-
队列容量设置需要权衡:太小容易导致生产者阻塞,太大可能消耗过多内存
-
使用poll(timeout)替代take()可以避免永久阻塞:
java复制String log = queue.poll(1, TimeUnit.SECONDS);
if (log == null) {
// 超时处理逻辑
}
- 批量操作drainTo()可以一次性取出所有元素,提高效率:
java复制List<String> logs = new ArrayList<>();
queue.drainTo(logs); // 非阻塞
- 注意处理InterruptedException,正确的中断处理方式是恢复中断状态:
java复制try {
queue.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// 清理工作
}
4. 定时器:精准的任务调度
4.1 Java定时任务方案对比
在开发对账系统时,我们需要每天凌晨2点执行对账任务。最初使用简单的Thread.sleep()实现,结果发现时间误差越来越大。后来改用ScheduledExecutorService,终于实现了精准的定时调度。
Java中常见的定时任务实现方式:
- Timer:单线程执行所有任务
java复制Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("Task executed at: " + new Date());
}
}, 1000, 2000); // 延迟1秒,每2秒执行一次
缺点:一个任务抛出异常会影响其他任务
- ScheduledExecutorService:线程池实现
java复制ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
executor.scheduleAtFixedRate(() -> {
System.out.println("Task executed at: " + new Date());
}, 1, 2, TimeUnit.SECONDS);
优点:支持多线程,更灵活可靠
- Spring @Scheduled:基于注解的简化方式
java复制@Scheduled(cron = "0 0 2 * * ?")
public void reconciliation() {
// 对账逻辑
}
4.2 定时任务最佳实践
-
固定速率(scheduleAtFixedRate)vs 固定延迟(scheduleWithFixedDelay):
- 固定速率:无论任务执行时间长短,都按固定间隔触发
- 固定延迟:任务执行完成后,延迟固定时间再触发下一次
-
处理定时任务中的异常:
java复制executor.scheduleAtFixedRate(() -> {
try {
// 业务逻辑
} catch (Exception e) {
// 记录日志
// 发送告警
}
}, 1, 2, TimeUnit.SECONDS);
-
分布式环境下使用Quartz或Elastic-Job等框架,避免多节点重复执行
-
对于精确度要求高的任务,要考虑时钟同步问题(NTP服务)
5. 线程池:并发编程的基石
5.1 为什么需要线程池
在一次性能优化中,我发现系统中有大量短生命周期的线程创建和销毁,消耗了大量资源。通过引入线程池,不仅提升了性能,还避免了资源耗尽的风险。
线程池的核心优势:
- 降低资源消耗:复用已创建的线程
- 提高响应速度:任务到达时线程已存在
- 提高线程可管理性:统一分配、调优和监控
5.2 线程池关键参数详解
ThreadPoolExecutor的7个核心参数:
java复制public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 工作队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
参数配置经验:
- CPU密集型任务:核心线程数 = CPU核数 + 1
- IO密集型任务:核心线程数 = CPU核数 * 2
- 混合型任务:拆分不同线程池处理
5.3 四种常用线程池
- FixedThreadPool:固定大小线程池
java复制ExecutorService executor = Executors.newFixedThreadPool(5);
- CachedThreadPool:可扩容线程池
java复制ExecutorService executor = Executors.newCachedThreadPool();
- SingleThreadExecutor:单线程池
java复制ExecutorService executor = Executors.newSingleThreadExecutor();
- ScheduledThreadPool:定时任务线程池
java复制ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
注意:阿里巴巴Java开发手册建议手动创建ThreadPoolExecutor,而非使用Executors工厂方法,避免OOM风险
5.4 自定义线程池实战
来看一个电商订单处理的线程池配置:
java复制ThreadPoolExecutor orderExecutor = new ThreadPoolExecutor(
4, // 核心线程数
8, // 最大线程数
30, TimeUnit.SECONDS, // 空闲线程存活时间
new ArrayBlockingQueue<>(1000), // 使用有界队列
new CustomThreadFactory("Order-Thread"), // 自定义线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 自定义线程工厂
class CustomThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger threadNumber = new AtomicInteger(1);
public CustomThreadFactory(String namePrefix) {
this.namePrefix = namePrefix;
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, namePrefix + "-" + threadNumber.getAndIncrement());
t.setDaemon(false);
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
5.5 线程池监控与调优
- 监控关键指标:
java复制// 获取线程池状态
int activeCount = executor.getActiveCount();
long completedTaskCount = executor.getCompletedTaskCount();
int poolSize = executor.getPoolSize();
int queueSize = executor.getQueue().size();
- 使用Hook记录任务执行时间:
java复制executor.submit(() -> {
long start = System.currentTimeMillis();
try {
// 业务逻辑
} finally {
long cost = System.currentTimeMillis() - start;
monitor.record("TaskCost", cost);
}
});
- 优雅关闭线程池:
java复制executor.shutdown(); // 停止接收新任务
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 强制终止
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
6. 多线程编程常见陷阱
6.1 线程安全问题
- 竞态条件:检查后执行(check-then-act)操作
java复制// 不安全的实现
if (map.containsKey(key)) {
map.remove(key);
}
// 安全的实现
map.remove(key);
- 内存可见性:使用volatile保证可见性
java复制private volatile boolean running = true;
public void stop() {
running = false;
}
- 死锁:按固定顺序获取锁
java复制// 错误的写法:可能产生死锁
public void transfer(Account from, Account to, int amount) {
synchronized (from) {
synchronized (to) {
// 转账逻辑
}
}
}
// 正确的写法:按固定顺序获取锁
public void transfer(Account from, Account to, int amount) {
Account first = from.hashCode() < to.hashCode() ? from : to;
Account second = from.hashCode() < to.hashCode() ? to : from;
synchronized (first) {
synchronized (second) {
// 转账逻辑
}
}
}
6.2 性能问题
- 锁竞争:减小锁粒度
java复制// 粗粒度锁
public synchronized void process() {
// 所有操作都在同步块内
}
// 细粒度锁
public void process() {
// 非同步操作
synchronized (this) {
// 只有必要操作同步
}
// 非同步操作
}
-
上下文切换:避免创建过多线程
-
伪共享:使用@Contended注解(Java 8+)
java复制@Contended
private volatile long value;
6.3 资源管理问题
-
线程泄漏:确保线程池中的任务不会无限期阻塞
-
资源未释放:使用try-finally确保资源释放
java复制public void process() {
lock.lock();
try {
// 业务逻辑
} finally {
lock.unlock();
}
}
- 任务堆积:合理设置队列容量和拒绝策略
7. Java并发工具类进阶
7.1 CountDownLatch:多线程协调
模拟并行处理多个文件:
java复制void processFiles(List<File> files) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(4);
CountDownLatch latch = new CountDownLatch(files.size());
for (File file : files) {
executor.execute(() -> {
try {
processFile(file);
} finally {
latch.countDown();
}
});
}
latch.await(); // 等待所有文件处理完成
executor.shutdown();
}
7.2 CyclicBarrier:循环屏障
模拟多阶段任务:
java复制void multiPhaseTask(int nThreads) {
CyclicBarrier barrier = new CyclicBarrier(nThreads,
() -> System.out.println("All threads reached barrier"));
for (int i = 0; i < nThreads; i++) {
new Thread(() -> {
try {
phase1();
barrier.await();
phase2();
barrier.await();
phase3();
} catch (Exception e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
7.3 CompletableFuture:异步编程
链式异步调用:
java复制CompletableFuture.supplyAsync(() -> fetchUserInfo(userId))
.thenApplyAsync(user -> calculateCreditScore(user))
.thenAcceptAsync(score -> updateCreditDatabase(score))
.exceptionally(ex -> {
log.error("Error processing credit score", ex);
return null;
});
8. 多线程调试技巧
8.1 线程堆栈分析
使用jstack命令获取线程转储:
bash复制jstack <pid> > thread_dump.txt
分析线程状态:
- RUNNABLE:正在执行
- BLOCKED:等待获取锁
- WAITING:无限期等待
- TIMED_WAITING:有限期等待
8.2 使用VisualVM监控
- 安装VisualVM插件:Threads Inspector
- 监控线程创建和销毁
- 检测死锁
8.3 日志记录技巧
为每个线程添加唯一标识:
java复制private static final ThreadLocal<String> threadId = ThreadLocal.withInitial(
() -> "Thread-" + Thread.currentThread().getId()
);
void log(String message) {
System.out.println(threadId.get() + ": " + message);
}
9. 性能优化实战
9.1 减少锁竞争
- 使用读写锁(ReentrantReadWriteLock)替代独占锁
- 使用并发集合(ConcurrentHashMap)替代同步集合
- 使用原子变量(AtomicInteger)替代锁
9.2 合理设置线程池参数
根据业务特点调整:
- CPU密集型:小线程池+大队列
- IO密集型:大线程池+小队列
- 混合型:隔离不同任务到不同线程池
9.3 使用Fork/Join框架处理分治任务
计算斐波那契数列:
java复制class FibonacciTask extends RecursiveTask<Integer> {
final int n;
FibonacciTask(int n) { this.n = n; }
protected Integer compute() {
if (n <= 1) return n;
FibonacciTask f1 = new FibonacciTask(n - 1);
f1.fork();
FibonacciTask f2 = new FibonacciTask(n - 2);
return f2.compute() + f1.join();
}
}
ForkJoinPool pool = new ForkJoinPool();
int result = pool.invoke(new FibonacciTask(10));
10. 项目实战:设计一个高并发订单系统
10.1 系统架构设计
- 订单接收层:使用NIO处理高并发连接
- 订单处理层:线程池+阻塞队列实现异步处理
- 库存管理层:分布式锁保证库存一致性
- 支付对接层:异步回调处理支付结果
10.2 核心代码实现
订单处理服务:
java复制public class OrderProcessor {
private final ExecutorService executor;
private final BlockingQueue<Order> queue;
private final InventoryService inventory;
public OrderProcessor(int poolSize, int queueSize) {
this.executor = new ThreadPoolExecutor(
poolSize, poolSize, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(queueSize),
new OrderThreadFactory(),
new OrderRejectedHandler()
);
this.queue = new ArrayBlockingQueue<>(queueSize);
this.inventory = new InventoryService();
startWorkers();
}
private void startWorkers() {
for (int i = 0; i < executor.getCorePoolSize(); i++) {
executor.execute(this::processOrder);
}
}
public void submitOrder(Order order) {
try {
if (!queue.offer(order, 1, TimeUnit.SECONDS)) {
throw new OrderException("System busy, please try later");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new OrderException("Submission interrupted");
}
}
private void processOrder() {
while (!Thread.currentThread().isInterrupted()) {
try {
Order order = queue.take();
inventory.reserve(order.getItems());
PaymentService.process(order);
order.setStatus(OrderStatus.COMPLETED);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
} catch (Exception e) {
log.error("Order processing failed", e);
}
}
}
public void shutdown() {
executor.shutdown();
try {
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
10.3 性能优化点
- 订单批处理:将多个订单合并处理,减少数据库IO
- 缓存预热:提前加载热点商品数据
- 读写分离:查询操作使用只读副本
- 异步日志:使用Disruptor实现高性能日志记录
11. 多线程测试策略
11.1 单元测试
使用JUnit测试并发代码:
java复制@Test
public void testConcurrentAccess() throws InterruptedException {
final Counter counter = new Counter();
final int threads = 10;
final int iterations = 1000;
ExecutorService executor = Executors.newFixedThreadPool(threads);
CountDownLatch latch = new CountDownLatch(threads);
for (int i = 0; i < threads; i++) {
executor.execute(() -> {
try {
for (int j = 0; j < iterations; j++) {
counter.increment();
}
} finally {
latch.countDown();
}
});
}
latch.await();
assertEquals(threads * iterations, counter.getValue());
}
11.2 压力测试
使用JMeter模拟并发请求:
- 配置线程组:100并发,持续5分钟
- 添加HTTP请求采样器
- 添加聚合报告监听器
11.3 死锁检测
使用ThreadMXBean检测死锁:
java复制ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long[] threadIds = bean.findDeadlockedThreads();
if (threadIds != null) {
ThreadInfo[] infos = bean.getThreadInfo(threadIds);
for (ThreadInfo info : infos) {
System.out.println(info);
}
}
12. Java内存模型与happens-before
12.1 内存可见性保证
happens-before规则:
- 程序顺序规则
- 监视器锁规则
- volatile变量规则
- 线程启动规则
- 线程终止规则
- 中断规则
- 终结器规则
- 传递性
12.2 final字段的特殊语义
正确发布的不可变对象:
java复制class ImmutableObject {
private final int value;
public ImmutableObject(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
12.3 安全发布模式
- 通过静态初始化器
- 通过volatile字段
- 通过final字段
- 通过正确锁定的字段
13. 并发设计模式进阶
13.1 Worker Thread模式
实现一个简单的Worker Thread:
java复制class Worker implements Runnable {
private final BlockingQueue<Runnable> tasks;
public Worker(BlockingQueue<Runnable> tasks) {
this.tasks = tasks;
}
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
Runnable task = tasks.take();
task.run();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
}
13.2 Thread-Per-Message模式
为每个请求创建新线程:
java复制class Server {
public void serve(Request request) {
new Thread(() -> {
handleRequest(request);
}).start();
}
private void handleRequest(Request request) {
// 处理请求
}
}
注意:实际项目中应使用线程池而非直接创建线程
13.3 Producer-Consumer模式
使用管道连接生产者和消费者:
java复制class Pipe {
private final BlockingQueue<Data> queue = new LinkedBlockingQueue<>();
public void put(Data data) throws InterruptedException {
queue.put(data);
}
public Data take() throws InterruptedException {
return queue.take();
}
}
14. Java并发集合详解
14.1 ConcurrentHashMap
- 分段锁设计:Java 7及之前版本
- CAS优化:Java 8及之后版本
- 原子操作:
java复制ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>();
map.compute("key", (k, v) -> v == null ? 1L : v + 1L);
14.2 CopyOnWriteArrayList
适用读多写少场景:
java复制List<String> list = new CopyOnWriteArrayList<>();
list.add("item"); // 创建新数组副本
String item = list.get(0); // 无需同步
14.3 ConcurrentLinkedQueue
无界非阻塞队列:
java复制Queue<String> queue = new ConcurrentLinkedQueue<>();
queue.offer("item");
String item = queue.poll();
14.4 BlockingQueue实现对比
| 实现类 | 有界性 | 锁类型 | 适用场景 |
|---|---|---|---|
| ArrayBlockingQueue | 有界 | 单锁 | 固定大小缓冲 |
| LinkedBlockingQueue | 可选 | 双锁 | 无界或有界缓冲 |
| PriorityBlockingQueue | 无界 | 单锁 | 优先级排序 |
| SynchronousQueue | 无界 | CAS | 直接传递 |
| DelayQueue | 无界 | 单锁 | 延时任务 |
15. 实战:设计一个异步任务框架
15.1 需求分析
- 支持提交异步任务
- 支持任务回调
- 支持任务进度查询
- 支持任务取消
- 支持任务优先级
15.2 核心设计
java复制public class AsyncTaskExecutor {
private final PriorityBlockingQueue<AsyncTask> taskQueue;
private final ThreadPoolExecutor executor;
public AsyncTaskExecutor(int corePoolSize, int maxPoolSize) {
this.taskQueue = new PriorityBlockingQueue<>();
this.executor = new ThreadPoolExecutor(
corePoolSize, maxPoolSize,
60L, TimeUnit.SECONDS,
taskQueue
);
}
public <T> Future<T> submit(Callable<T> task, int priority) {
AsyncTask<T> asyncTask = new AsyncTask<>(task, priority);
executor.execute(asyncTask);
return asyncTask;
}
private static class AsyncTask<T> implements RunnableFuture<T>, Comparable<AsyncTask<?>> {
private final Callable<T> task;
private final int priority;
private volatile T result;
private volatile Exception exception;
private volatile boolean cancelled;
public AsyncTask(Callable<T> task, int priority) {
this.task = task;
this.priority = priority;
}
@Override
public void run() {
try {
if (!cancelled) {
result = task.call();
}
} catch (Exception e) {
exception = e;
}
}
@Override
public int compareTo(AsyncTask<?> other) {
return Integer.compare(other.priority, this.priority);
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
cancelled = true;
return true;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public boolean isDone() {
return result != null || exception != null || cancelled;
}
@Override
public T get() throws InterruptedException, ExecutionException {
while (!isDone()) {
Thread.sleep(100);
}
if (exception != null) {
throw new ExecutionException(exception);
}
if (cancelled) {
throw new CancellationException();
}
return result;
}
@Override
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
long endTime = System.nanoTime() + unit.toNanos(timeout);
while (!isDone() && System.nanoTime() < endTime) {
Thread.sleep(100);
}
if (!isDone()) {
throw new TimeoutException();
}
if (exception != null) {
throw new ExecutionException(exception);
}
if (cancelled) {
throw new CancellationException();
}
return result;
}
}
}
15.3 使用示例
java复制AsyncTaskExecutor executor = new AsyncTaskExecutor(4, 8);
// 提交高优先级任务
Future<String> future = executor.submit(() -> {
// 耗时操作
return "Result";
}, 1);
// 获取结果
String result = future.get();
16. Java 8+并发新特性
16.1 CompletableFuture组合操作
java复制CompletableFuture.supplyAsync(() -> fetchUser(userId))
.thenCombine(
CompletableFuture.supplyAsync(() -> fetchOrder(orderId)),
(user, order) -> createInvoice(user, order)
)
.thenAcceptAsync(invoice -> sendEmail(invoice))
.exceptionally(ex -> {
log.error("Process failed", ex);
return null;
});
16.2 StampedLock优化读写锁
java复制class Point {
private double x, y;
private final StampedLock lock = new StampedLock();
void move(double deltaX, double deltaY) {
long stamp = lock.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
lock.unlockWrite(stamp);
}
}
double distanceFromOrigin() {
long stamp = lock.tryOptimisticRead();
double currentX = x, currentY = y;
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try {
currentX = x;
currentY = y;
} finally {
lock.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
}
16.3 并行流(Parallel Stream)
java复制List<Order> orders = getOrders();
double total = orders.parallelStream()
.filter(o -> o.getStatus() == OrderStatus.COMPLETED)
.mapToDouble(Order::getAmount)
.sum();
17. 多线程编程规范
17.1 命名规范
- 线程和线程池名称要有意义:
java复制ThreadFactory factory = new ThreadFactory() {
private final AtomicInteger counter = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "Order-Processor-" + counter.getAndIncrement());
}
};
- 线程组分类管理:
java复制ThreadGroup serviceGroup = new ThreadGroup("Service Threads");
ThreadGroup workerGroup = new ThreadGroup("Worker Threads");
17.2 异常处理
- 不要吞没InterruptedException:
java复制try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// 清理工作
}
- 为线程设置未捕获异常处理器:
java复制Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
log.error("Uncaught exception in thread: " + t.getName(), e);
});
17.3 资源清理
- 使用try-with-resources管理资源:
java复制try (Connection conn = getConnection()) {
// 使用连接
}
- 确保线程池正确关闭:
java复制Runtime.getRuntime().addShutdownHook(new Thread(() -> {
executor.shutdown();
try {
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}));
18. 常见面试问题解析
18.1 基础概念
-
线程 vs 进程:
- 进程是资源分配的最小单位
- 线程是CPU调度的最小单位
- 同一进程的线程共享内存空间
-
并发 vs 并行:
- 并发:逻辑上同时发生
- 并行:物理上同时执行
18.2 同步机制
-
synchronized实现原理:
- 基于对象监视器(Monitor)
- 方法级同步使用ACC_SYNCHRONIZED标志
- 代码块同步使用monitorenter/monitorexit指令
-
volatile作用:
- 保证可见性
- 防止指令重排序
- 不保证原子性
18.3 线程池调优
-
如何设置核心线程数:
- CPU密集型:CPU核数 + 1
- IO密集型:CPU核数 * (1 + 平均等待时间/平均计算时间)
-
拒绝策略选择:
- AbortPolicy:默认策略,抛出异常
- CallerRunsPolicy:由调用线程执行
- DiscardPolicy:静默丢弃
- DiscardOldestPolicy:丢弃队列最老任务
19. 性能监控与诊断
19.1 JVM工具
- jstack:线程堆栈分析
- jconsole:可视化监控
- VisualVM:综合性能分析
- Arthas:在线诊断工具
19.2 关键指标
-
线程状态分布:
- Runnable:理想状态
- Blocked:锁竞争严重
- Waiting:可能存在问题
-
**CPU使用率