发布日期:2025-10-09 20:40 点击次数:133
获课:bcwit.top/14295
获取ZY↑↑方打开链接↑↑
在Java生态中,并发编程是构建高可用、高性能系统的关键技能。无论是电商平台的秒杀系统、分布式任务调度,还是金融交易的高并发处理,都离不开对线程、锁、原子操作及AQS(AbstractQueuedSynchronizer)的深入理解。基于“博学谷Java并发编程原理精讲”课程的核心内容,系统梳理Java并发编程的底层原理与关键机制,帮助读者建立完整的知识框架,避开常见陷阱。
一、线程:并发编程的基石
1. 线程的本质与创建方式
线程与进程的区别:进程是资源分配的最小单位,线程是CPU调度的最小单位;线程共享进程内存空间(如堆、方法区),进程间通信需通过IPC(进程间通信),线程间可直接访问共享变量(需同步机制)。
Java线程的创建:继承Thread类或实现Runnable接口(推荐,避免单继承限制);通过ExecutorService线程池管理线程(如FixedThreadPool、CachedThreadPool),避免频繁创建销毁的开销。
2. 线程的生命周期与状态转换
六种状态:NEW:线程创建但未启动;RUNNABLE:可运行状态(包括就绪和运行中);BLOCKED:等待获取锁(如进入同步块);WAITING:无限期等待(如调用Object.wait());TIMED_WAITING:限时等待(如Thread.sleep());TERMINATED:线程执行完毕。
状态转换触发条件:start()方法调用使线程从NEW转为RUNNABLE;锁竞争失败时进入BLOCKED;调用wait()/join()进入WAITING或TIMED_WAITING。
3. 线程通信与协作
共享变量问题:多线程读写共享变量可能导致数据不一致(如计数器累加错误);解决方案:同步机制(锁、原子类)或线程封闭(ThreadLocal)。
协作机制:wait()/notify():基于对象监视器(Monitor)的等待-通知机制;Condition接口:更灵活的线程等待与唤醒(如ReentrantLock.newCondition());CountDownLatch/CyclicBarrier:同步工具类,协调多个线程的执行顺序。
二、锁:同步的核心武器
1. 锁的分类与适用场景
悲观锁与乐观锁:悲观锁:认为冲突必然发生,每次操作都加锁(如synchronized、ReentrantLock);乐观锁:认为冲突较少发生,通过版本号或CAS(Compare-And-Swap)实现(如AtomicInteger)。
公平锁与非公平锁:公平锁:按请求顺序分配锁(避免线程饥饿);非公平锁:允许插队(提高吞吐量,但可能加剧饥饿)。
可重入锁与不可重入锁:可重入锁:同一线程可多次获取锁(如ReentrantLock、synchronized);不可重入锁:获取锁后需释放才能再次获取(较少使用)。
2. synchronized与ReentrantLock的对比
特性
synchronized
ReentrantLock
实现方式
JVM内置关键字,依赖Monitor对象
JDK实现,基于AQS框架
锁类型
默认非公平锁,可改为公平锁(JVM参数)
支持公平/非公平锁
可中断性
不可中断(等待锁的线程无法响应中断)
支持lockInterruptibly()可中断锁获取
超时机制
无
支持tryLock(long, TimeUnit)
条件变量
wait()/notify()
Condition.await()/signal()
适用场景
简单同步需求,代码简洁
需要灵活控制(如可中断、超时)
3. 锁的优化策略
锁粗化:将多次连续的锁操作合并为一次(如循环内同步块外提);
锁消除:JVM通过逃逸分析,发现无共享数据竞争时移除锁(如局部变量同步);
自旋锁:线程短暂等待(循环检查锁状态),避免线程切换开销(适用于锁持有时间短的场景)。
三、原子操作:无锁编程的基石
1. CAS(Compare-And-Swap)原理
底层机制:通过CPU指令(如x86的CMPXCHG)实现原子性;操作步骤:比较内存值与预期值,若相等则更新为新值,否则失败重试。
ABA问题:现象:值从A变为B又变回A,CAS会误认为未变化;解决方案:使用AtomicStampedReference(带版本号的原子引用)。
2. Java原子类体系
基本类型原子类:AtomicInteger、AtomicLong、AtomicBoolean;示例:无锁计数器、自增操作。
数组原子类:AtomicIntegerArray、AtomicLongArray;适用场景:数组元素的原子更新。
引用类型原子类:AtomicReference:通用对象引用原子更新;AtomicMarkableReference:带标记位的原子引用(解决ABA问题)。
3. 原子操作的应用场景
计数器统计:如并发环境下的请求计数、库存扣减;
状态标志:如线程是否完成的原子标记;
无锁数据结构:如无锁队列(基于CAS实现入队出队)。
四、AQS(AbstractQueuedSynchronizer):锁与同步器的核心框架
1. AQS的设计思想
模板方法模式:AQS定义同步状态的获取与释放框架,子类(如ReentrantLock、Semaphore)实现具体逻辑;核心方法:tryAcquire()、tryRelease()、isHeldExclusively()。
CLH队列:使用双向链表实现等待队列,每个节点存储线程与状态(WAITING/CANCELLED);线程获取锁失败时入队,通过自旋等待前驱节点释放锁。
2. AQS的同步状态管理
State变量:32位整型,表示同步状态(如ReentrantLock中为重入次数,Semaphore中为可用许可数);通过CAS更新状态,保证原子性。
独占模式与共享模式:独占模式:同一时刻仅一个线程可获取锁(如ReentrantLock);共享模式:多个线程可同时获取锁(如Semaphore、CountDownLatch)。
3. AQS的子类实现解析
ReentrantLock:独占模式,通过Sync内部类实现公平/非公平锁;重入时增加State值,释放时递减。
Semaphore:共享模式,通过Sync管理许可数;acquire()减少许可,release()增加许可。
CountDownLatch:共享模式,初始化时设置计数器;countDown()递减计数器,await()阻塞直到计数器归零。
4. AQS的底层流程
获取锁:调用acquire(),尝试tryAcquire();失败则创建节点入队,挂起当前线程。
释放锁:调用release(),更新State值;唤醒后继节点线程。
线程唤醒:通过LockSupport.unpark()激活挂起的线程;被唤醒线程重新尝试获取锁。
五、并发编程的常见问题与解决方案
1. 死锁的预防与检测
死锁条件:互斥条件、占有并等待、非抢占、循环等待。
预防策略:避免嵌套锁(按固定顺序获取锁);设置锁超时(tryLock(long, TimeUnit));使用ThreadMXBean检测死锁(findDeadlockedThreads())。
2. 线程安全的实现方式
不可变对象:如String、Integer,天然线程安全;
线程封闭:如ThreadLocal,每个线程独享变量副本;
同步容器:如Vector、Hashtable(性能较差);
并发容器:如ConcurrentHashMap、CopyOnWriteArrayList(分段锁、写时复制)。
3. 性能优化建议
减少锁粒度:如分段锁(ConcurrentHashMap的16段);
避免锁竞争:使用读写锁(ReentrantReadWriteLock,读多写少场景);
无锁化设计:优先使用CAS或原子类,减少线程阻塞。
六、构建高并发系统的核心原则
明确同步需求:根据场景选择锁、原子操作或无锁设计;
控制锁范围:缩小同步块代码,减少持有锁时间;
优先使用并发工具:如CountDownLatch、CyclicBarrier、Semaphore;
测试与监控:通过压力测试(如JMeter)验证并发性能,使用JVisualVM监控线程状态。
从原理到实践的并发编程进阶之路
Java并发编程的复杂度源于对线程调度、内存可见性、指令重排序等底层机制的抽象。通过掌握线程生命周期、锁的分类与优化、原子操作原理及AQS框架,开发者能够设计出高效、稳定的并发系统。无论是应对高并发流量还是解决复杂同步问题,这些核心知识都是不可或缺的武器库。