Java并发编程:JUC核心组件与实战技巧
1. JUC并发编程概述Java并发编程工具包Java Util Concurrent简称JUC是Java 5.0引入的重要特性它为开发者提供了一套完整的并发编程解决方案。在单核CPU时代多线程主要解决的是IO阻塞问题而在多核CPU时代JUC则能真正实现并行计算充分利用硬件资源。JUC主要包含以下核心组件原子类Atomic如AtomicInteger、AtomicReference等锁机制Locks如ReentrantLock、ReadWriteLock等并发容器如ConcurrentHashMap、CopyOnWriteArrayList等线程池Executor如ThreadPoolExecutor、ScheduledThreadPool等同步工具如CountDownLatch、CyclicBarrier等2. 线程基础与JUC核心类2.1 线程创建方式对比传统创建线程的方式有两种继承Thread类实现Runnable接口JUC提供了第三种方式实现Callable接口。与Runnable相比Callable可以返回结果和抛出异常。class MyTask implements CallableInteger { Override public Integer call() throws Exception { // 耗时计算 return 42; } } public static void main(String[] args) throws Exception { FutureTaskInteger task new FutureTask(new MyTask()); new Thread(task).start(); Integer result task.get(); // 阻塞获取结果 }2.2 核心同步工具类2.2.1 CountDownLatch倒计时门栓适用于一个线程等待多个线程完成的场景CountDownLatch latch new CountDownLatch(3); for (int i 0; i 3; i) { new Thread(() - { // 执行任务 latch.countDown(); }).start(); } latch.await(); // 等待所有线程完成 System.out.println(所有任务完成);2.2.2 CyclicBarrier循环栅栏适用于多个线程相互等待的场景CyclicBarrier barrier new CyclicBarrier(3, () - { System.out.println(所有线程到达屏障); }); for (int i 0; i 3; i) { new Thread(() - { // 第一阶段工作 barrier.await(); // 第二阶段工作 }).start(); }2.2.3 Semaphore信号量用于控制同时访问特定资源的线程数量Semaphore semaphore new Semaphore(3); // 允许3个线程同时访问 for (int i 0; i 10; i) { new Thread(() - { try { semaphore.acquire(); // 访问资源 } finally { semaphore.release(); } }).start(); }3. 并发容器详解3.1 ConcurrentHashMap相比Hashtable和Collections.synchronizedMap()ConcurrentHashMap采用了更细粒度的锁机制JDK7分段锁SegmentJDK8CAS synchronizedConcurrentHashMapString, Integer map new ConcurrentHashMap(); map.computeIfAbsent(key, k - 1); // 线程安全的复合操作3.2 CopyOnWriteArrayList写时复制的List实现适合读多写少的场景CopyOnWriteArrayListString list new CopyOnWriteArrayList(); list.add(item); // 写操作会复制整个数组 String item list.get(0); // 读操作无锁4. 线程池深度解析4.1 线程池参数详解ThreadPoolExecutor的核心参数corePoolSize核心线程数maximumPoolSize最大线程数keepAliveTime空闲线程存活时间unit时间单位workQueue工作队列threadFactory线程工厂handler拒绝策略4.2 四种拒绝策略AbortPolicy默认抛出RejectedExecutionExceptionCallerRunsPolicy由调用线程执行该任务DiscardPolicy直接丢弃任务DiscardOldestPolicy丢弃队列中最老的任务4.3 线程池最佳实践int coreSize Runtime.getRuntime().availableProcessors(); ThreadPoolExecutor executor new ThreadPoolExecutor( coreSize, coreSize * 2, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue(1000), new ThreadPoolExecutor.CallerRunsPolicy() );5. 锁机制进阶5.1 ReentrantLock vs synchronized特性ReentrantLocksynchronized实现API级别JVM级别公平锁支持不支持条件变量支持有限支持中断响应支持不支持性能高竞争时更好低竞争时更好5.2 ReadWriteLock读写分离锁提高读多写少场景的性能ReadWriteLock rwLock new ReentrantReadWriteLock(); Lock readLock rwLock.readLock(); Lock writeLock rwLock.writeLock(); // 读操作 readLock.lock(); try { // 读取数据 } finally { readLock.unlock(); } // 写操作 writeLock.lock(); try { // 修改数据 } finally { writeLock.unlock(); }6. 原子类与CAS6.1 基本类型原子类AtomicIntegerAtomicLongAtomicBooleanAtomicInteger counter new AtomicInteger(0); counter.incrementAndGet(); // 原子自增6.2 引用类型原子类AtomicReferenceAtomicStampedReference解决ABA问题AtomicReferenceString ref new AtomicReference(old); ref.compareAndSet(old, new); // CAS操作7. 并发编程实践技巧尽量使用并发容器而非同步容器合理设置线程池参数锁的粒度要尽可能小避免锁嵌套防止死锁使用ThreadLocal保存线程私有数据优先使用volatile而非锁实现可见性考虑使用Fork/Join框架处理分治任务8. 常见问题排查8.1 死锁检测使用jstack工具可以检测死锁jstack pid8.2 线程池问题线程饥饿调整队列大小和拒绝策略内存泄漏确保任务不会持有不必要的引用性能问题合理设置核心和最大线程数8.3 内存可见性问题使用volatile或适当的同步确保可见性class Singleton { private static volatile Singleton instance; public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }9. JUC性能调优减少锁竞争缩小临界区、使用读写锁避免虚假共享使用Contended注解合理使用无锁算法如原子类线程池大小设置CPU密集型核心数1IO密集型核心数*210. Java内存模型JMMJMM定义了线程与主内存的交互规则关键概念包括原子性synchronized、Lock可见性volatile、final有序性happens-before原则volatile变量的写-读建立了happens-before关系// 线程A volatile boolean flag false; x 42; flag true; // 线程B if (flag) { // 这里能看到x42 }11. CompletableFuture异步编程Java8引入的CompletableFuture简化了异步编程CompletableFuture.supplyAsync(() - { // 异步任务 return result; }).thenApplyAsync(result - { // 后续处理 return result.toUpperCase(); }).exceptionally(ex - { // 异常处理 return fallback; });12. 并发测试工具JMH微基准测试JUnit单元测试Stress测试压力测试示例JMH测试BenchmarkMode(Mode.Throughput) OutputTimeUnit(TimeUnit.SECONDS) public class MyBenchmark { Benchmark public void testMethod() { // 测试代码 } }13. 实际案例分析13.1 高性能计数器class Counter { private final AtomicLong count new AtomicLong(0); private final LongAdder adder new LongAdder(); // 简单计数 public void increment() { count.incrementAndGet(); } // 高并发计数 public void incrementFast() { adder.increment(); } }13.2 缓存实现class CacheK, V { private final ConcurrentHashMapK, FutureTaskV map new ConcurrentHashMap(); public V get(K key, CallableV loader) throws Exception { while (true) { FutureTaskV future map.get(key); if (future ! null) { return future.get(); } FutureTaskV newTask new FutureTask(loader); FutureTaskV oldTask map.putIfAbsent(key, newTask); if (oldTask null) { newTask.run(); return newTask.get(); } else { return oldTask.get(); } } } }14. Java并发演进Java5引入JUCJava7Fork/Join框架Java8CompletableFuture、StampedLockJava9改进的并发APIJava11新的HTTP客户端支持异步Java17虚拟线程预览15. 最佳实践总结理解业务场景选择合适的并发工具优先使用高级并发工具而非低级别同步编写无状态或线程封闭的代码避免过早优化先保证正确性充分测试并发场景监控线程状态和资源使用情况保持代码简洁和可维护性