Java 并发编程最佳实践:构建高并发、可伸缩的应用

张开发
2026/4/5 20:56:01 15 分钟阅读

分享文章

Java 并发编程最佳实践:构建高并发、可伸缩的应用
Java 并发编程最佳实践构建高并发、可伸缩的应用别叫我大神叫我 Alex 就好。一、引言大家好我是 Alex。在现代应用开发中并发编程已经成为一个不可或缺的技能。随着多核处理器的普及充分利用系统资源、提高应用性能成为了开发者的重要目标。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适合读多写少的场景StampedLockJDK 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 MapString, User userMap new ConcurrentHashMap(); // 读多写少 private final ListUser userList new CopyOnWriteArrayList(); // 生产者-消费者 private final BlockingQueueMessage 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// 异步执行 CompletableFutureString future1 CompletableFuture.supplyAsync(() - { // 执行任务 return Result 1; }); CompletableFutureString future2 CompletableFuture.supplyAsync(() - { // 执行任务 return Result 2; }); // 组合结果 CompletableFutureString 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 ConcurrentHashMapString, 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 并发编程。如果你有任何问题欢迎在评论区留言。关于作者我是 Alex一个在 CSDN 写 Java 架构思考的暖男。喜欢手冲咖啡养了一只叫Java的拉布拉多。如果我的文章对你有帮助欢迎关注我一起探讨 Java 技术的优雅之道。

更多文章