一、引言
在现代应用开发中,并发编程已经成为一个不可或缺的技能。随着多核处理器的普及,充分利用系统资源、提高应用性能成为了开发者的重要目标。java 作为企业级应用的主流语言,提供了丰富的并发编程工具和 api。今天,我想和大家分享一下 java 并发编程的最佳实践,帮助大家构建高并发、可伸缩的应用。
二、并发编程基础
1. 线程安全
- 原子性:操作要么全部执行,要么全部不执行
- 可见性:一个线程对共享变量的修改,其他线程能够立即看到
- 有序性:程序执行的顺序按照代码的先后顺序执行
2. 并发工具类
基本工具:
- synchronized:同步关键字
- volatile:保证可见性
- final:不可变对象
并发集合:
- concurrenthashmap:线程安全的哈希表
- copyonwritearraylist:写时复制的数组
- blockingqueue:阻塞队列
线程池:
- executors:线程池工厂
- threadpoolexecutor:线程池实现
三、最佳实践
1. 线程池使用
合理配置线程池:
// 好的做法
threadpoolexecutor executor = new threadpoolexecutor(
corepoolsize, // 核心线程数
maximumpoolsize, // 最大线程数
keepalivetime, // 线程存活时间
timeunit.seconds, // 时间单位
new linkedblockingqueue<>(queuecapacity), // 任务队列
new threadfactorybuilder().setnameformat("worker-%d").build(), // 线程工厂
new threadpoolexecutor.callerrunspolicy() // 拒绝策略
);
// 不好的做法
executorservice executor = executors.newfixedthreadpool(10); // 可能导致 oom
线程池监控:
@bean
public threadpooltaskexecutor taskexecutor() {
threadpooltaskexecutor executor = new threadpooltaskexecutor();
executor.setcorepoolsize(10);
executor.setmaxpoolsize(20);
executor.setqueuecapacity(100);
executor.setthreadnameprefix("task-");
executor.setrejectedexecutionhandler(new threadpoolexecutor.callerrunspolicy());
executor.initialize();
return executor;
}2. 锁的使用
选择合适的锁:
- synchronized:适合简单场景
- reentrantlock:适合复杂场景,支持公平锁和非公平锁
- readwritelock:适合读多写少的场景
- stampedlock:jdk 8+ 提供,性能更好
锁优化:
- 减少锁的范围:只锁定必要的代码块
- 使用无锁数据结构:如 atomic 类
- 避免死锁:合理安排锁的获取顺序
示例:
// 好的做法
public class counter {
private final atomicinteger count = new atomicinteger(0);
public void increment() {
count.incrementandget();
}
public int getcount() {
return count.get();
}
}
// 不好的做法
public class counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getcount() {
return count;
}
}3. 并发集合使用
选择合适的集合:
| 场景 | 推荐集合 |
|---|---|
| 高并发读写 | concurrenthashmap |
| 读多写少 | copyonwritearraylist |
| 生产者-消费者 | blockingqueue |
| 线程安全队列 | concurrentlinkedqueue |
示例:
// 高并发读写 private final map<string, user> usermap = new concurrenthashmap<>(); // 读多写少 private final list<user> userlist = new copyonwritearraylist<>(); // 生产者-消费者 private final blockingqueue<message> messagequeue = new linkedblockingqueue<>();
4. 线程安全的单例模式
枚举单例:
// 最佳实践
public enum singleton {
instance;
private final userservice userservice;
singleton() {
userservice = new userservice();
}
public userservice getuserservice() {
return userservice;
}
}
// 使用
userservice userservice = singleton.instance.getuserservice();双重检查锁:
public class singleton {
private static volatile singleton instance;
private singleton() {}
public static singleton getinstance() {
if (instance == null) {
synchronized (singleton.class) {
if (instance == null) {
instance = new singleton();
}
}
}
return instance;
}
}5. 原子操作
atomic 类:
- atomicinteger:原子整数
- atomiclong:原子长整型
- atomicreference:原子引用
- atomicstampedreference:带版本号的原子引用
示例:
// 原子更新 atomicinteger counter = new atomicinteger(0); // 递增 int value = counter.incrementandget(); // 比较并交换 boolean updated = counter.compareandset(expectedvalue, newvalue);
6. 并发工具
countdownlatch:
// 等待多个线程完成
countdownlatch latch = new countdownlatch(3);
for (int i = 0; i < 3; i++) {
executor.submit(() -> {
try {
// 执行任务
} finally {
latch.countdown();
}
});
}
// 等待所有任务完成
latch.await();cyclicbarrier:
// 等待多个线程到达屏障
cyclicbarrier barrier = new cyclicbarrier(3, () -> {
system.out.println("所有线程已到达屏障");
});
for (int i = 0; i < 3; i++) {
executor.submit(() -> {
try {
// 执行任务
barrier.await();
// 继续执行
} catch (exception e) {
e.printstacktrace();
}
});
}semaphore:
// 控制并发访问数
semaphore semaphore = new semaphore(5);
for (int i = 0; i < 10; i++) {
executor.submit(() -> {
try {
semaphore.acquire();
try {
// 执行任务
} finally {
semaphore.release();
}
} catch (interruptedexception e) {
e.printstacktrace();
}
});
}completablefuture:
// 异步执行
completablefuture<string> future1 = completablefuture.supplyasync(() -> {
// 执行任务
return "result 1";
});
completablefuture<string> future2 = completablefuture.supplyasync(() -> {
// 执行任务
return "result 2";
});
// 组合结果
completablefuture<string> combined = future1.thencombine(future2, (result1, result2) -> {
return result1 + ", " + result2;
});
// 获取结果
string result = combined.join();四、并发编程的挑战
1. 死锁
避免死锁:
- 按固定顺序获取锁
- 使用超时机制
- 使用 lock 接口的 trylock() 方法
示例:
// 好的做法:按固定顺序获取锁
public void transfermoney(account from, account to, int amount) {
int fromhash = system.identityhashcode(from);
int tohash = system.identityhashcode(to);
if (fromhash < tohash) {
synchronized (from) {
synchronized (to) {
from.debit(amount);
to.credit(amount);
}
}
} else if (fromhash > tohash) {
synchronized (to) {
synchronized (from) {
from.debit(amount);
to.credit(amount);
}
}
} else {
// 使用额外的锁
synchronized (lock) {
synchronized (from) {
synchronized (to) {
from.debit(amount);
to.credit(amount);
}
}
}
}
}2. 活锁
避免活锁:
- 引入随机延迟
- 使用优先级机制
- 避免无限重试
3. 饥饿
避免饥饿:
- 使用公平锁
- 合理设置线程优先级
- 避免长时间持有锁
五、实战案例
案例:电商系统库存管理
需求:
- 高并发下的库存管理
- 避免超卖
- 保证数据一致性
实现:
public class inventoryservice {
private final concurrenthashmap<string, atomicinteger> inventory = new concurrenthashmap<>();
public boolean decreasestock(string productid, int quantity) {
atomicinteger stock = inventory.computeifabsent(productid, k -> new atomicinteger(0));
while (true) {
int currentstock = stock.get();
if (currentstock < quantity) {
return false; // 库存不足
}
if (stock.compareandset(currentstock, currentstock - quantity)) {
return true; // 成功减少库存
}
// 竞争失败,重试
}
}
public int getstock(string productid) {
atomicinteger stock = inventory.get(productid);
return stock != null ? stock.get() : 0;
}
public void increasestock(string productid, int quantity) {
atomicinteger stock = inventory.computeifabsent(productid, k -> new atomicinteger(0));
stock.addandget(quantity);
}
}六、总结
java 并发编程是一个复杂但强大的工具,通过合理使用并发工具和最佳实践,我们可以构建高并发、可伸缩的应用。在实践中,我们需要根据具体场景选择合适的并发策略,避免常见的并发问题,确保应用的正确性和性能。
这其实可以更优雅一点。
到此这篇关于java并发编程最佳实践之构建高并发、可伸缩的应用实例的文章就介绍到这了,更多相关java构建高并发、可伸缩内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论