Java并发编程实战
# Java并发编程实战
# 前言
随着多核处理器的普及,并发编程变得越来越重要。良好的并发设计可以充分利用系统资源,提高程序性能。本文将分享Java并发编程的核心概念、常见问题及实战经验。
# Java并发编程基础
# 线程的生命周期
Java线程的生命周期包括以下状态:
- NEW:新创建的线程,尚未启动
- RUNNABLE:可运行状态,包括就绪和运行中
- BLOCKED:阻塞状态,等待获取锁
- WAITING:等待状态,无限期等待另一个线程的特定操作
- TIMED_WAITING:超时等待状态,在指定时间内等待
- TERMINATED:终止状态,线程执行完毕
# 线程的创建方式
Java中创建线程的几种方式:
// 1. 继承Thread类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread running...");
}
}
// 使用:new MyThread().start();
// 2. 实现Runnable接口
Runnable task = () -> System.out.println("Runnable task running...");
// 使用:new Thread(task).start();
// 3. 实现Callable接口(可返回结果)
Callable<String> call = () -> {
return "Callable result";
};
FutureTask<String> future = new FutureTask<>(call);
// 使用:new Thread(future).start(); String result = future.get();
// 4. 使用线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> System.out.println("Task in thread pool"));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 并发编程的挑战
# 线程安全问题
在多线程环境下,共享变量的并发访问可能导致数据不一致:
// 非线程安全的计数器
public class Counter {
private int count = 0;
public void increment() {
count++; // 非原子操作
}
public int getCount() {
return count;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 死锁
当两个或多个线程互相等待对方持有的锁时,会发生死锁:
public void deadlockDemo() {
Object lock1 = new Object();
Object lock2 = new Object();
Thread t1 = new Thread(() -> {
synchronized(lock1) {
sleep(100); // 确保线程2有机会获取lock2
synchronized(lock2) {
System.out.println("Thread 1 acquired both locks");
}
}
});
Thread t2 = new Thread(() -> {
synchronized(lock2) {
sleep(100);
synchronized(lock1) {
System.out.println("Thread 2 acquired both locks");
}
}
});
t1.start();
t2.start();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Java并发工具类
# 同步容器
Java提供了多种线程安全的集合类:
// 线程安全的List
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
List<String> concurrentList = new CopyOnWriteArrayList<>();
// 线程安全的Map
Map<String, String> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
Map<String, String> concurrentMap = new ConcurrentHashMap<>();
// 线程安全的Set
Set<String> synchronizedSet = Collections.synchronizedSet(new HashSet<>());
Set<String> concurrentSet = new CopyOnWriteArraySet<>();
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 锁机制
Java提供了多种锁机制:
// ReentrantLock
ReentrantLock lock = new ReentrantLock();
try {
lock.lock();
// 临界区代码
} finally {
lock.unlock(); // 确保锁被释放
}
// ReadWriteLock
ReadWriteLock rwLock = new ReentrantReadWriteLock();
// 读操作,允许并发
rwLock.readLock().lock();
try {
// 读取共享数据
} finally {
rwLock.readLock().unlock();
}
// 写操作,独占锁
rwLock.writeLock().lock();
try {
// 修改共享数据
} finally {
rwLock.writeLock().unlock();
}
// StampedLock(Java 8)
StampedLock lock = new StampedLock();
// 乐观读
long stamp = lock.tryOptimisticRead();
// 读取数据
if (!lock.validate(stamp)) {
// 数据已被修改,转为悲观读
stamp = lock.readLock();
try {
// 重新读取数据
} finally {
lock.unlockRead(stamp);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 线程池最佳实践
# 线程池的创建
推荐使用ThreadPoolExecutor自定义线程池,而不是Executors工厂方法:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲线程存活时间
new ArrayBlockingQueue<>(100), // 工作队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
1
2
3
4
5
6
7
2
3
4
5
6
7
# 线程池的关闭
正确关闭线程池:
executor.shutdown(); // 平滑关闭,等待任务完成
boolean terminated = executor.awaitTermination(30, TimeUnit.SECONDS);
if (!terminated) {
executor.shutdownNow(); // 强制关闭
}
1
2
3
4
5
2
3
4
5
# 案例分析:高并发订单系统优化
# 问题描述
某电商平台订单系统在秒杀活动期间出现严重性能问题:
- 系统响应缓慢
- 数据库连接耗尽
- 部分订单处理失败
# 优化方案
- 使用线程池处理订单:
// 创建合适大小的线程池
ThreadPoolExecutor orderExecutor = new ThreadPoolExecutor(
10, 20, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(500),
new ThreadPoolExecutor.CallerRunsPolicy()
);
// 异步处理订单
public void processOrder(Order order) {
orderExecutor.submit(() -> {
try {
// 订单处理逻辑
saveOrder(order);
notifyPayment(order);
} catch (Exception e) {
// 异常处理
logError(e);
retryQueue.add(order);
}
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- 使用读写锁优化库存查询:
public class InventoryService {
private Map<Long, Integer> inventory = new ConcurrentHashMap<>();
private ReadWriteLock lock = new ReentrantReadWriteLock();
public Integer getStock(Long productId) {
lock.readLock().lock();
try {
return inventory.get(productId);
} finally {
lock.readLock().unlock();
}
}
public void updateStock(Long productId, int delta) {
lock.writeLock().lock();
try {
Integer current = inventory.getOrDefault(productId, 0);
inventory.put(productId, current + delta);
} finally {
lock.writeLock().unlock();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 使用CompletableFuture并行处理订单步骤:
public CompletableFuture<Order> processOrderAsync(Order order) {
return CompletableFuture.supplyAsync(() -> {
// 验证订单
validateOrder(order);
return order;
}).thenApplyAsync(validOrder -> {
// 扣减库存
reduceInventory(validOrder);
return validOrder;
}).thenApplyAsync(validOrder -> {
// 生成支付信息
createPayment(validOrder);
return validOrder;
}).exceptionally(ex -> {
// 异常处理
handleOrderException(order, ex);
return null;
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 优化效果
- 系统吞吐量提升300%
- 平均响应时间从1.2秒降至0.3秒
- 订单处理成功率从92%提升至99.9%
# 总结与建议
- 合理使用线程池:根据业务特点和硬件资源配置合适的线程池参数
- 选择合适的并发工具:针对不同场景选择合适的并发容器和同步工具
- 避免过度同步:同步会带来性能开销,只在必要时使用
- 防止死锁:合理设计锁的获取顺序,使用带超时的锁获取方法
- 优化锁粒度:减小锁的范围,提高并发度
- 使用无锁算法:在适当场景下使用CAS等无锁算法提高性能
Java并发编程是一个复杂的主题,需要不断学习和实践。希望本文能为你的并发编程之路提供一些帮助。
上次更新: 2025/07/01, 01:17:09