Java并发编程深度进阶:从synchronized到AQS框架

143次阅读
没有评论

引言

并发编程是Java开发中的高级主题,也是区分熟练开发者与专家级开发者的重要分水岭。在当今多核处理器普及的时代,能否高效地编写并发程序直接影响着应用的可扩展性和性能表现。本文将从Java内存模型的基础理论出发,逐步深入到锁机制、并发工具和AQS框架,带您全面掌握Java并发编程的核心知识体系。

Java内存模型(JMM)与并发基础

Java内存模型(Java Memory Model,JMM)是理解Java并发编程的基石。JMM定义了多线程环境下变量的访问规则——何时一个线程对共享变量的写入对另一个线程可见。JMM的关键抽象是主内存(Main Memory)和工作内存(Working Memory)的概念:所有变量存储在主内存中,每个线程有自己的工作内存(实际上对应CPU缓存和寄存器),线程对变量的所有操作必须在工作内存中进行,然后同步回主内存。

这种内存模型带来了可见性(Visibility)、原子性(Atomicity)和有序性(Ordering)三大并发编程挑战。可见性问题指一个线程修改了变量,其他线程可能看不到这个修改;原子性问题指复合操作(如i++)在执行过程中可能被打断;有序性问题指编译器或处理器可能对指令进行重排序优化,导致代码执行顺序与编写顺序不同。

happens-before原则是JMM中定义操作间偏序关系的关键概念。如果操作A happens-before操作B,那么A的结果对B可见,且A的执行顺序在B之前。常见的happens-before关系包括:程序次序规则(同一个线程内)、volatile变量规则、锁规则(解锁happens-before加锁)、传递性规则等。

synchronized关键字深入

synchronized是Java中最基础的同步机制,从JDK 1.0起就存在。它通过对象头中的Mark Word来实现锁的获取和释放。在JDK 6之前,synchronized是重量级操作,直接依赖操作系统的互斥量(Mutex)。JDK 6之后引入了锁升级(Lock Escalation)机制,包含偏向锁(Biased Locking)、轻量级锁(Lightweight Locking)和重量级锁(Heavyweight Locking)三个层次。

偏向锁针对一个线程多次获取同一个锁的场景进行优化——当锁处于偏向模式时,持有偏向锁的线程再次进入同步块时无需任何同步操作。轻量级锁通过CAS(Compare-And-Swap)操作在线程栈中复制Mark Word来实现,适用于线程交替执行同步块的场景。重量级锁则是最后的兜底方案,依赖操作系统的互斥量。

需要注意的是,在JDK 15中偏向锁默认被禁用,JDK 18中被标记为废弃,JDK 21中已完全移除。这是因为在现代高并发应用中,偏向锁带来的维护成本往往超过了其收益。这一变化提醒我们,随着JVM的演进,一些长期存在的”最佳实践”可能需要重新评估。

AbstractQueuedSynchronizer(AQS)框架

AQS是Java并发包(java.util.concurrent)的核心基础,ReentrantLock、Semaphore、CountDownLatch、ReentrantReadWriteLock等几乎所有的高级同步工具都基于AQS构建。AQS提供了一个基于FIFO等待队列的框架,用于实现阻塞锁和相关同步器。

AQS的核心是一个int类型的volatile变量state,代表同步状态。通过父类方法(tryAcquire、tryRelease等)操作状态,通过子类定义状态的具体语义(比如ReentrantLock中state表示锁被重入的次数)。AQS内部维护了一个基于CLH队列变体的等待队列,未能获取同步状态的线程会被加入队列中等待。

深入理解AQS对于掌握Java并发编程至关重要。它不仅展示了模板方法模式在框架设计中的精妙应用,也提供了构建自定义同步器的强大基础。当需要实现某种特殊的同步语义而现有的并发工具无法满足时,了解AQS可以让你自信地构建自己的解决方案。

并发工具类实战

java.util.concurrent包提供了丰富的高层并发工具。ConcurrentHashMap通过分段锁(JDK 7)和CAS+红黑树(JDK 8+)实现了高并发下的线程安全性,是Hashtable和Collections.synchronizedMap的现代替代品。CopyOnWriteArrayList适合读多写少的场景,通过在写操作时复制底层数组来实现线程安全。

线程池(ThreadPoolExecutor)是管理任务执行的核心工具。FixedThreadPool适合负载稳定的场景,CachedThreadPool适合大量短期异步任务,ScheduledThreadPool用于定时和周期性任务。线程池的参数配置需要仔细考量——核心线程数、最大线程数、队列类型和拒绝策略的选择都会影响系统的行为和性能。

CompletableFuture是Java 8引入的组合式异步编程工具,它比Future更加强大和灵活。通过thenApply、thenCompose、thenCombine等方法,可以将多个异步操作串联或并联起来,构建复杂的异步处理管道。exceptionally和handle方法提供了优雅的异常处理机制。

正文完
 0
评论(没有评论)