Java锁相关知识与并发优化全总结
在Java并发编程中,锁是解决线程安全问题的核心机制,用于控制多个线程对共享资源的访问,避免出现竞态条件、数据不一致等问题。从基础的synchronized锁,到基于AQS实现的ReentrantLock、CountDownLatch等,锁的实现不断优化,适配不同的并发场景。本文将系统梳理Java锁的基础知识、核心底层框架AQS原理、基于AQS实现的各类锁,以及锁在并发场景中的优化方案,为Java并发编程提供全面参考。
一、Java锁相关基础知识
Java中的锁本质是一种同步机制,通过限制多个线程对共享资源的并发访问,保证数据的原子性、可见性和有序性(Java内存模型JMM的三大特性)。理解锁的基础概念和分类,是掌握并发编程的前提。
1.1 锁的核心概念
-
线程安全:多个线程并发访问共享资源时,不会出现数据错乱、不一致的情况,核心是保证原子性、可见性、有序性。
-
原子性:一个操作或多个操作,要么全部执行且执行过程中不被中断,要么全部不执行(如i++并非原子操作,需锁或CAS保证)。
-
可见性:一个线程修改共享变量后,其他线程能立即看到修改后的结果(如volatile关键字可保证可见性,锁也能间接保证)。
-
有序性:线程执行顺序符合代码逻辑,避免指令重排序导致的并发问题(锁可禁止重排序,volatile可禁止部分重排序)。
竞态条件:多个线程同时操作共享资源,且操作结果依赖于线程执行顺序,导致最终结果不确定的现象,是锁需要解决的核心问题。
临界区:包含共享资源操作的代码块,锁的核心作用是保证临界区的互斥访问,同一时刻只有一个线程能进入临界区。
1.2 锁的分类(按核心维度)
1.2.1 按锁的互斥性分类
-
独占锁(排他锁):同一时刻只有一个线程能持有锁,其他线程需等待锁释放后才能竞争(如synchronized、ReentrantLock)。
-
共享锁:同一时刻多个线程可同时持有锁,适合读多写少的场景(如ReentrantReadWriteLock的读锁、Semaphore)。
1.2.2 按锁的获取机制分类
-
乐观锁:假设没有线程竞争共享资源,先执行操作,执行完成后检查是否有冲突,若有冲突则重试(如CAS、版本号机制,无锁实现)。
-
悲观锁:假设存在线程竞争,获取资源前先获取锁,确保只有自己能操作资源,避免冲突(如synchronized、ReentrantLock)。
1.2.3 按锁的可重入性分类
-
可重入锁:同一线程可多次获取同一把锁,避免死锁(如synchronized、ReentrantLock,均为可重入锁)。例如:线程A获取锁后,在锁的临界区内再次调用需要同一把锁的方法,可直接获取锁,无需等待。
-
不可重入锁:同一线程多次获取同一把锁时会阻塞,容易导致死锁(如早期的LockSupport实现的简单锁,实际开发中极少使用)。
1.2.4 按锁的底层实现分类
-
内置锁(synchronized):JVM层面实现的锁,自动释放,使用简单,但灵活性差。
-
显式锁(Lock接口):Java代码层面实现的锁,需手动获取和释放(try-finally保证),灵活性高,支持中断、超时、条件变量等特性(如ReentrantLock、ReentrantReadWriteLock)。
1.3 基础锁实现:synchronized详解
synchronized是Java最基础的内置锁,无需手动释放,由JVM自动管理,可用于修饰方法、代码块,保证临界区的互斥访问。
1.3.1 synchronized的使用场景
-
修饰实例方法:锁对象是当前实例(this),同一时刻只有一个线程能执行该实例的该方法。
-
修饰静态方法:锁对象是当前类的Class对象,同一时刻只有一个线程能执行该类的所有静态同步方法。
-
修饰代码块:锁对象由开发者指定(如synchronized(obj)),灵活控制临界区范围,减少锁的粒度。
1.3.2 synchronized的底层实现(JDK1.8)
JDK1.6之前,synchronized是重量级锁,依赖操作系统的互斥量(Mutex)实现,切换线程时需切换内核态和用户态,性能较差;JDK1.6及以后,JVM对synchronized进行了优化,引入了偏向锁、轻量级锁、重量级锁的升级机制,提升性能。
-
偏向锁:针对单线程访问场景,锁会偏向于第一个获取它的线程,后续该线程再次获取锁时,无需竞争,直接获取(通过对象头的偏向锁标记实现),减少锁竞争的开销。
-
轻量级锁:当有两个线程竞争锁时,偏向锁升级为轻量级锁,线程通过CAS操作尝试获取锁,无需进入内核态,减少上下文切换开销。
-
重量级锁:当多个线程竞争锁,且CAS操作失败时,轻量级锁升级为重量级锁,依赖操作系统的互斥量实现,此时线程会阻塞,等待锁释放(性能最差,但稳定性最高)。
核心原理:synchronized的锁对象(实例、Class对象)的对象头中,包含锁状态标记(偏向锁、轻量级锁、重量级锁),JVM通过修改对象头的锁状态,实现锁的获取和释放。
二、锁的底层实现框架AQS原理
AQS(AbstractQueuedSynchronizer,抽象队列同步器)是Java并发包(java.util.concurrent)中锁的核心底层框架,ReentrantLock、CountDownLatch、Semaphore等锁的实现,均基于AQS。AQS的核心思想是“模板方法模式”,定义了锁的获取、释放的核心流程,子类只需实现少量抽象方法,即可实现不同类型的锁。
2.1 AQS的核心结构
AQS的核心由两部分组成:状态变量和同步队列,二者协同工作,实现锁的竞争与释放。
2.1.1 状态变量(state)
AQS内部维护一个volatile修饰的int类型变量state,用于表示锁的状态,不同类型的锁对state的定义不同:
-
独占锁(如ReentrantLock):state=0表示锁未被持有,state>0表示锁被持有(state的值等于重入次数,可重入锁特性)。
-
共享锁(如ReentrantReadWriteLock的读锁):state的高16位表示读锁的持有次数,低16位表示写锁的持有次数。
state的修改通过CAS操作保证原子性,AQS提供了getState()、setState()、compareAndSetState()三个方法,用于操作state变量。
2.1.2 同步队列(CLH队列)
当多个线程竞争锁失败时,会被封装为Node节点,加入到AQS的同步队列(CLH队列,一种双向链表队列)中,等待锁释放。同步队列的核心作用是“排队等待”,实现线程的有序竞争。
-
Node节点:每个Node节点包含线程引用、等待状态(如CANCELLED、SIGNAL等)、前驱节点(prev)、后继节点(next)。
-
队列结构:头节点(head)是持有锁的线程节点,其他节点是等待锁的线程节点;当锁释放时,头节点会唤醒后继节点,让其尝试获取锁。
2.2 AQS的核心流程(模板方法)
AQS定义了锁的获取和释放的核心流程,子类通过实现抽象方法,适配不同的锁类型(独占锁、共享锁)。核心模板方法分为两类:独占式获取/释放锁、共享式获取/释放锁。
2.2.1 独占式获取锁(acquire(int arg))
独占式获取锁的核心流程(以ReentrantLock为例),适用于同一时刻只有一个线程能获取锁的场景:
-
调用tryAcquire(int arg)方法:子类实现,尝试获取锁(如ReentrantLock中,判断state是否为0,若为0则通过CAS修改state为1,获取锁;若state不为0,判断当前线程是否为持有锁的线程,若是则state加1,实现可重入)。
-
若tryAcquire成功,直接返回,线程获取锁并执行临界区代码。
-
若tryAcquire失败,调用addWaiter(Node.EXCLUSIVE)方法,将当前线程封装为独占式Node节点,加入同步队列尾部。
-
调用acquireQueued(Node node, int arg)方法,让节点在队列中自旋等待,直到获取锁;若等待过程中线程被中断,会记录中断状态,待获取锁后再处理中断。
2.2.2 独占式释放锁(release(int arg))
独占式释放锁的核心流程,用于释放锁并唤醒等待队列中的线程:
-
调用tryRelease(int arg)方法:子类实现,尝试释放锁(如ReentrantLock中,将state减1,若state减为0,则释放锁,返回true;若state不为0,返回false,表示还处于重入状态)。
-
若tryRelease成功,获取同步队列的头节点,调用unparkSuccessor(Node node)方法,唤醒头节点的后继节点,让其尝试获取锁。
-
释放锁完成,线程退出临界区。
2.2.3 共享式获取/释放锁
共享式获取/释放锁适用于多个线程可同时获取锁的场景(如ReentrantReadWriteLock的读锁),核心流程与独占式类似,但多了“共享计数”的逻辑:
-
共享式获取锁(acquireShared(int arg)):调用tryAcquireShared(int arg)方法,尝试获取共享锁,若返回值>=0,表示获取成功;若返回值<0,表示获取失败,加入同步队列等待。
-
共享式释放锁(releaseShared(int arg)):调用tryReleaseShared(int arg)方法,尝试释放共享锁,释放成功后,唤醒队列中所有等待的共享锁线程。
2.3 AQS的核心抽象方法(子类实现)
AQS定义了5个抽象方法,子类需根据锁的类型(独占/共享),实现对应的方法,其余流程由AQS的模板方法完成:
-
tryAcquire(int arg):独占式获取锁,返回true表示获取成功,false表示失败。
-
tryRelease(int arg):独占式释放锁,返回true表示释放成功,false表示未完全释放(如重入锁未释放完重入次数)。
-
tryAcquireShared(int arg):共享式获取锁,返回值>=0表示获取成功(返回值表示剩余可用的共享锁数量),<0表示失败。
-
tryReleaseShared(int arg):共享式释放锁,返回true表示释放成功,false表示未完全释放。
-
isHeldExclusively():判断当前线程是否独占式持有锁,用于可重入锁的判断。
2.4 AQS的ConditionObject(条件变量)
ConditionObject是AQS的内部类,实现了Condition接口,用于配合Lock锁实现线程间的协作(类似synchronized中的wait()、notify()方法),核心作用是让持有锁的线程暂时释放锁,进入等待状态,待条件满足后再被唤醒,重新获取锁。
2.4.1 ConditionObject的核心特性
-
与Lock绑定:每个ConditionObject都与一个Lock锁(基于AQS实现)绑定,通过Lock的newCondition()方法创建,一个Lock可以创建多个ConditionObject,实现不同条件下的线程协作。
-
等待队列:ConditionObject内部维护一个独立的等待队列(单向链表),与AQS的同步队列相互独立;调用await()方法的线程,会释放锁并加入该等待队列,等待被signal()/signalAll()唤醒。
-
核心方法:
-
await():当前线程释放锁,进入等待队列,直到被唤醒并重新获取锁;可响应中断。
-
await(long time, TimeUnit unit):带超时的等待,超时未被唤醒则自动返回,避免无限等待。
-
signal():唤醒等待队列中的一个线程,该线程会加入AQS的同步队列,尝试获取锁。
-
signalAll():唤醒等待队列中的所有线程,所有线程都会加入AQS的同步队列,竞争获取锁。
-
2.4.2 底层实现逻辑
ConditionObject的等待队列与AQS的同步队列协同工作,核心流程如下:
-
线程获取Lock锁(通过AQS的acquire方法)后,调用Condition的await()方法,会释放当前持有的锁(调用AQS的release方法),并将自身封装为Condition等待队列的节点,加入等待队列。
-
其他线程获取锁后,调用Condition的signal()方法,会将等待队列的头节点移出,加入AQS的同步队列,让其参与锁的竞争。
-
被唤醒的线程在AQS同步队列中自旋等待,获取到锁后,继续执行await()方法之后的代码。
注意:调用Condition的await()、signal()、signalAll()方法前,线程必须持有对应的Lock锁,否则会抛出IllegalMonitorStateException异常(与synchronized中wait()、notify()需在同步块中调用一致)。
三、基于AQS实现的其他锁
Java并发包中,大部分锁和同步工具类都基于AQS实现,它们通过实现AQS的抽象方法,适配不同的并发场景,以下是最常用的几种实现。
3.1 ReentrantLock(可重入独占锁)
ReentrantLock是基于AQS实现的显式独占锁,与synchronized功能类似,支持可重入特性,但灵活性更高,提供了中断、超时、公平锁/非公平锁等特性。
3.1.1 核心特性
-
可重入:同一线程可多次获取锁,state记录重入次数,释放时需对应次数的释放。
-
公平锁与非公平锁:
-
公平锁:线程获取锁的顺序与加入队列的顺序一致,避免线程饥饿,但性能略差(通过AQS的同步队列实现)。
-
非公平锁:线程获取锁时,先尝试CAS获取锁,若失败再加入队列,可能导致先加入队列的线程后获取锁,但性能更好(默认是非公平锁)。
-
-
中断支持:支持线程在等待锁时被中断(lockInterruptibly()方法),避免线程无限等待。
-
超时获取:支持设置超时时间(tryLock(long timeout, TimeUnit unit)方法),若超时未获取到锁,返回false,避免死锁。
3.1.2 基于AQS的实现逻辑
-
state变量:表示锁的重入次数,state=0表示未持有锁,state>0表示持有锁,每次重入state加1,释放时state减1。
-
tryAcquire(int arg):非公平锁中,先尝试CAS修改state为1(未持有锁时),若成功则获取锁;若state不为0,判断当前线程是否为持有锁的线程,若是则state加1。公平锁中,需先判断队列中是否有前驱节点,若无再尝试CAS获取锁。
-
tryRelease(int arg):将state减1,若state减为0,返回true,表示完全释放锁;否则返回false。
3.2 ReentrantReadWriteLock(可重入读写锁)
ReentrantReadWriteLock是基于AQS实现的读写分离锁,支持多个线程同时读,同一时刻只有一个线程能写,适用于“读多写少”的并发场景,能提升并发性能(读操作无互斥,写操作与读、写操作互斥)。
3.2.1 核心特性
-
读写分离:读锁(共享锁)和写锁(独占锁)分离,读锁可多个线程同时持有,写锁只能单个线程持有。
-
可重入:读锁和写锁均支持可重入,读锁重入时,读锁计数加1;写锁重入时,写锁计数加1。
-
互斥规则:写锁与读锁互斥、写锁与写锁互斥、读锁与读锁不互斥。
-
降级支持:写锁可降级为读锁(先获取写锁,再获取读锁,最后释放写锁),但读锁不能升级为写锁(避免死锁)。
3.2.2 基于AQS的实现逻辑
ReentrantReadWriteLock内部维护两个锁:ReadLock(读锁)和WriteLock(写锁),共享同一个AQS实例,通过state变量的高16位和低16位分别记录读锁和写锁的持有次数:
-
state高16位:读锁持有次数(readCount),每次获取读锁,readCount加1;释放读锁,readCount减1。
-
state低16位:写锁持有次数(writeCount),每次获取写锁,writeCount加1;释放写锁,writeCount减1。
-
写锁获取(tryAcquire):判断readCount是否为0且writeCount为0,若满足则通过CAS修改writeCount,获取写锁;若当前线程持有写锁,则writeCount加1(可重入)。
-
读锁获取(tryAcquireShared):判断writeCount是否为0,若为0则通过CAS修改readCount,获取读锁;若当前线程持有写锁,则可直接获取读锁(写锁降级)。
3.3 CountDownLatch(倒计时锁存器)
CountDownLatch是基于AQS实现的同步工具类,用于等待多个线程完成任务后,主线程再继续执行,核心是“倒计时”机制,不可重复使用(倒计时到0后,无法重置)。
3.3.1 核心特性
-
初始化时指定倒计时次数(count),count由AQS的state变量维护。
-
多个线程调用countDown()方法,每次调用count减1(state减1)。
-
主线程调用await()方法,会阻塞直到count减为0(state为0),然后继续执行。
3.3.2 基于AQS的实现逻辑
-
state变量:表示剩余的倒计时次数,初始化时state=count。
-
await()方法:调用AQS的acquireSharedInterruptibly(1),尝试获取共享锁;tryAcquireShared返回state是否为0,若为0则获取成功(不阻塞),若不为0则加入同步队列等待。
-
countDown()方法:调用AQS的releaseShared(1),tryReleaseShared将state减1,若state减为0,则唤醒队列中所有等待的线程。
3.4 Semaphore(信号量)
Semaphore是基于AQS实现的共享锁工具类,用于控制同时访问共享资源的线程数量,核心是“信号量计数”,可重复使用,适用于限流场景。
3.4.1 核心特性
-
初始化时指定信号量数量(permits),permits由AQS的state变量维护,表示可同时访问的线程数量。
-
线程调用acquire()方法,获取一个信号量(state减1),若state为0则阻塞,等待其他线程释放信号量。
-
线程调用release()方法,释放一个信号量(state加1),唤醒队列中等待的线程。
-
支持公平锁和非公平锁,默认是非公平锁。
3.4.2 基于AQS的实现逻辑
-
state变量:表示剩余的信号量数量,初始化时state=permits。
-
acquire()方法:调用AQS的acquireSharedInterruptibly(1),tryAcquireShared判断state是否大于0,若是则state减1,获取成功;若否则加入队列等待。
-
release()方法:调用AQS的releaseShared(1),tryReleaseShared将state加1,唤醒队列中等待的线程。
3.5 CyclicBarrier(循环屏障)
CyclicBarrier是基于AQS实现的同步工具类,与CountDownLatch类似,用于多个线程协同执行,但核心区别是:CyclicBarrier可重复使用(屏障触发后可重置),且所有线程必须到达屏障点后,才能继续执行,适用于“多个线程相互等待,同步执行某个任务”的场景(如多线程分阶段执行任务,所有线程完成第一阶段后,再一起进入第二阶段)。
3.5.1 核心特性
-
可循环使用:当所有线程到达屏障点,触发屏障后,CyclicBarrier可通过reset()方法重置,再次使用(CountDownLatch不可重置)。
-
屏障动作:可在构造方法中指定一个Runnable对象,当所有线程到达屏障点时,会先执行该Runnable对象(屏障动作),再继续执行各个线程的后续代码。
-
初始化时指定“参与线程数”(parties),只有当参与的线程数全部到达屏障点(调用await()方法),屏障才会触发,所有线程继续执行。
-
支持超时等待:await(long timeout, TimeUnit unit),超时未到达屏障点的线程会抛出TimeoutException,屏障会被打破,其他等待的线程也会被唤醒并抛出异常。
3.5.2 基于AQS的实现逻辑
CyclicBarrier内部通过AQS的ConditionObject实现线程等待与唤醒,核心依赖两个核心变量:参与线程数(parties)、当前等待线程数(count),其中count由AQS的state变量维护:
-
state变量:表示当前等待到达屏障点的线程数,初始化时state=parties,每次有线程调用await()方法,state减1。
-
await()方法:调用后,当前线程进入Condition的等待队列,等待其他线程到达;当state减为0时,触发屏障动作(若有),然后唤醒所有等待的线程,并重置state为parties(实现循环使用)。
-
reset()方法:将state重置为parties,同时唤醒所有等待的线程,中断当前的屏障等待,以便重新开始新一轮的同步。
3.6 锁的实操案例(贴合实际开发)
以下案例覆盖synchronized、ReentrantLock、ReentrantReadWriteLock、CountDownLatch、CyclicBarrier的实际使用场景,结合业务场景说明锁的选型与使用,帮助快速落地。
3.6.1 案例1:synchronized实操(简单并发安全控制)
场景:电商订单接口,多线程同时创建订单,需保证订单号唯一(简单计数器实现),低并发场景下使用synchronized,简洁高效。
1 | /** |
说明:低并发场景下,synchronized无需手动释放锁,代码简洁,可满足线程安全需求;若并发量提升,可替换为ReentrantLock或AtomicInteger。
3.6.2 案例2:ReentrantLock实操(高并发+超时+中断)
场景:高并发场景下的库存扣减,需保证库存操作原子性,支持超时获取锁,避免死锁,同时支持中断等待。
1 | /** |
说明:高并发场景下,ReentrantLock的非公平锁性能优于synchronized,tryLock超时机制可有效避免死锁,finally中释放锁可防止锁泄漏。
3.6.3 案例3:ReentrantReadWriteLock实操(读多写少场景)
场景:商品详情查询服务,读操作频繁(用户查询商品),写操作稀少(管理员更新商品),使用读写锁提升并发性能。
1 | /** |
说明:读锁共享,多个读线程可同时执行,写锁独占,保证写操作的原子性;读多写少场景下,读写锁比独占锁性能提升显著。
3.6.4 案例4:CountDownLatch实操(多线程任务协同)
场景:主线程需要等待3个异步任务(查询用户、查询订单、查询商品)全部完成后,再汇总结果返回。
1 | /** |
说明:CountDownLatch不可重复使用,适合“一个线程等待多个线程完成任务”的场景,如主线程等待所有子任务完成后汇总结果。
3.6.5 案例5:CyclicBarrier实操(多线程循环协同)
场景:3个线程分两阶段执行任务,第一阶段所有线程完成后,执行屏障动作,再一起进入第二阶段,可循环执行多轮。
1 | /** |
说明:CyclicBarrier可重复使用,适合“多个线程分阶段协同执行”的场景,屏障动作可用于阶段汇总、日志记录等操作。
四、锁在并发中的优化
锁的使用能解决线程安全问题,但不当的使用会导致性能瓶颈(如锁竞争激烈、锁粒度太大等)。锁的优化核心是“减少锁竞争、降低锁开销”,结合业务场景选择合适的锁和优化方案,平衡线程安全和并发性能。
4.1 锁优化的核心原则
-
减少锁竞争:减少多个线程对同一把锁的竞争频率,降低阻塞时间。
-
降低锁开销:减少锁的获取、释放过程中的性能消耗(如避免重量级锁的频繁使用)。
-
合理控制锁粒度:锁粒度越小,并发度越高;但锁粒度太小会增加锁管理的开销,需平衡。
-
结合场景选择锁:根据“读多写少”“写多读少”“高并发”等场景,选择合适的锁类型(如读写锁、信号量等)。
4.2 具体优化方案
4.2.1 锁粒度优化:从粗粒度锁到细粒度锁
粗粒度锁:一把锁控制整个共享资源(如修饰整个方法),并发度低,锁竞争激烈;细粒度锁:将共享资源拆分,每部分用单独的锁控制,减少锁竞争。
示例:HashMap线程不安全,ConcurrentHashMap通过“分段锁”(JDK1.7)或“CAS+ synchronized”(JDK1.8)实现细粒度锁,将数组分段,每段用单独的锁控制,多个线程可同时操作不同分段,提升并发性能。
4.2.2 锁消除:消除不必要的锁
JVM的即时编译(JIT)会对代码进行优化,若检测到某个锁保护的是局部变量(不会被多线程共享),则会自动消除该锁,减少锁开销。
示例:方法内部创建的StringBuffer(线程安全,自带锁),若该StringBuffer仅在方法内部使用,不会被多线程访问,JVM会消除其内部的锁,提升性能(相当于使用StringBuilder)。
4.2.3 锁粗化:合并频繁的锁操作
若一段代码中频繁获取和释放同一把锁(如循环中多次获取锁),会增加锁的开销,JVM会自动将这些锁操作合并为一次获取和释放,减少锁的切换次数。
示例:循环中多次调用synchronized修饰的方法,JVM会优化为在循环外获取一次锁,循环结束后释放锁,避免频繁的锁获取/释放。
4.2.4 选择合适的锁类型
-
读多写少场景:使用ReentrantReadWriteLock,读锁共享,写锁独占,提升读操作的并发度。
-
高并发、低延迟场景:使用ReentrantLock(非公平锁),性能优于synchronized(尤其在高并发下)。
-
简单场景、低并发场景:使用synchronized,无需手动释放锁,代码简洁,维护成本低。
-
限流场景:使用Semaphore,控制同时访问的线程数量,避免资源过载。
-
多线程协作场景:使用CountDownLatch、CyclicBarrier等同步工具类,替代手动锁操作,提升代码可读性。
4.2.5 无锁优化:使用CAS替代锁
对于简单的原子操作(如计数器、状态更新),可使用CAS(Compare And Swap)操作替代锁,避免锁竞争的开销(CAS是乐观锁,无需阻塞线程)。
Java中提供了java.util.concurrent.atomic包,包含AtomicInteger、AtomicLong等原子类,内部基于CAS实现,可安全地实现原子操作,无需加锁。
4.2.6 避免死锁
死锁是锁使用中常见的问题,指多个线程互相持有对方需要的锁,导致无限阻塞。避免死锁的核心是“破坏死锁的四个必要条件”(互斥、持有并等待、不可剥夺、循环等待):
-
按顺序获取锁:多个线程获取多把锁时,按固定的顺序获取(如先获取锁A,再获取锁B),避免循环等待。
-
超时获取锁:使用ReentrantLock的tryLock(timeout)方法,若超时未获取到锁,释放已持有的锁,避免无限等待。
-
减少锁的持有时间:尽量缩短锁的临界区范围,获取锁后尽快执行核心逻辑,释放锁,减少其他线程的等待时间。
五、总结
Java锁是并发编程的核心,从基础的synchronized内置锁,到基于AQS框架实现的各类显式锁,锁的实现不断优化,适配不同的并发场景。AQS作为锁的底层核心框架,通过状态变量和同步队列,统一了锁的获取和释放流程,其内部的ConditionObject实现了线程间的灵活协作,子类只需实现少量抽象方法,即可快速实现不同类型的锁。
基于AQS实现的锁和同步工具类(ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore、CyclicBarrier),覆盖了大部分并发场景,掌握其核心特性和使用场景,能大幅提升并发编程的效率和安全性。结合实操案例,可快速将锁的知识落地到实际开发中,避免线程安全问题。
在实际并发开发中,锁的优化是提升系统性能的关键:合理控制锁粒度、选择合适的锁类型、避免死锁、使用无锁方案(CAS)等,能在保证线程安全的前提下,最大化提升并发性能。同时,需结合业务场景,权衡锁的安全性和性能,避免过度优化(如锁粒度太小导致的管理开销),才能写出高效、安全的并发代码。
(注:文档部分内容可能由 AI 生成)