摘要:本文深入解析Java锁机制,涵盖synchronized的monitor原理与锁升级过程、Lock的AQS实现原理,以及两者的演进对比与实战应用。适合需要深入理解Java并发编程、解决线程安全问题的开发者。
前言:为什么深入理解锁这么重要
早些年,曾接手过一个核心交易系统的性能优化任务。上线后用户反馈下单时偶尔会卡顿几秒钟,排查后发现是锁竞争导致的。系统用的是最原始的synchronized,在高并发场景下大量线程阻塞等待,严重拖慢了响应时间。
当时我对锁的理解停留在"加上synchronized就能保证线程安全"的层面。深入排查后才发现:锁不是简单的开关,而是有多种状态、多种实现、需要根据场景选择的复杂机制。合理使用锁,可以既保证安全又保证性能;不合理使用,可能成为系统瓶颈。
这篇文章会系统梳理Java锁的核心原理、演进历程、实战经验,帮你从"知道有锁"进阶到"真正会用锁"。
一、线程安全与锁的本质
1.1 为什么需要锁
多线程环境下,多个线程同时访问共享资源,可能出现数据不一致的问题。
经典例子:计数器
public class Counter {
private int count = 0;
public void increment() {
count++; // 看起来是一步操作,实际是三步
}
public int getCount() {
return count;
}
}
// 多线程并发调用
Counter counter = new Counter();
for (int i = 0; i < 1000; i++) {
new Thread(() -> counter.increment()).start();
}
// 结果可能小于1000count++的字节码:
1. getfield #2 // 读取count值到操作数栈
2. iinc #2 1 // 将count值加1
3. putfield #2 // 将新值写入count如果两个线程同时执行:
线程A读取count=0
线程B读取count=0
线程A加1,count=1
线程B加1,count=1(而不是预期的2)
最终结果丢失了一次增量
解决方法:加锁
锁的核心作用:保证同一时刻只有一个线程能执行临界区代码。
1.2 锁的核心概念
互斥性:同一时刻只有一个线程持有锁
可见性:锁释放前对共享变量的修改,在锁获取后对其他线程可见
可重入性:同一个线程可以多次获取同一把锁(避免死锁)
线程A获取锁 → 执行代码 → 再次获取锁(可重入)→ 执行代码 → 释放锁 → 释放锁公平性:
公平锁:按照请求顺序获取锁
非公平锁:允许插队,可能提高吞吐量但可能导致某些线程长时间等待
二、synchronized深入解析
2.1 synchronized的基本用法
三种使用方式:
// 1. 同步实例方法:锁当前对象
public synchronized void method() {
// 临界区
}
// 2. 同步静态方法:锁当前类的Class对象
public static synchronized void staticMethod() {
// 临界区
}
// 3. 同步代码块:锁指定对象
public void method() {
synchronized (this) { // 锁当前对象
// 临界区
}
synchronized (lockObject) { // 锁指定对象
// 临界区
}
}2.2 synchronized的底层原理
synchronized的实现依赖于JVM的**Monitor(监视器锁)**机制。
对象头结构
每个Java对象在堆内存中都有一个对象头,其中包含与锁相关的信息:

Mark Word在不同锁状态下的结构:

2.3 Monitor机制
Monitor是synchronized的重量级实现,依赖操作系统互斥量。
Monitor结构:

工作流程:

Monitor的代价:
Monitor依赖操作系统互斥量,涉及:
用户态与内核态切换
线程阻塞与唤醒
系统调用开销
这些操作成本高,所以传统synchronized被称为"重量级锁"。
2.4 锁升级(锁膨胀)过程
为了减少重量级锁的性能开销,JVM引入了锁升级机制:从偏向锁到轻量级锁再到重量级锁,逐步升级。
完整升级流程:

(1)偏向锁
原理:锁偏向第一个获取它的线程,后续该线程获取锁无需同步。
适用场景:只有一个线程反复获取锁(如单线程访问同步块)。
获取过程:
1. 检查Mark Word是否为偏向锁状态(biased_lock=1)
2. 检查线程ID是否为当前线程
→ 如果是,直接进入临界区(无需CAS)
→ 如果不是,尝试CAS替换线程ID
3. CAS成功,获得偏向锁
4. CAS失败,说明有竞争,撤销偏向锁撤销过程:
1. 等待全局安全点(没有正在执行的字节码)
2. 暂停拥有偏向锁的线程
3. 检查锁对象是否被锁定
→ 如果未被锁定,恢复为无锁状态
→ 如果被锁定,升级为轻量级锁
4. 恢复线程执行JDK 15的变化:
偏向锁在JDK 15中被废弃并默认禁用,原因:
复杂度高,维护困难
实际收益有限(现代应用多线程竞争普遍)
与其他优化(如逃逸分析)冲突
可以通过参数启用:-XX:+UseBiasedLocking(JDK 15之前默认开启)
(2)轻量级锁
原理:用CAS在栈帧中创建Lock Record,将Mark Word指向Lock Record。
适用场景:多个线程交替执行同步块,竞争不激烈。
获取过程:
1. 在当前线程栈帧中创建Lock Record
2. 用CAS将Mark Word替换为Lock Record的指针
3. CAS成功,获得轻量级锁
4. CAS失败,说明有竞争
→ 如果是当前线程,可重入(添加一条Lock Record)
→ 如果是其他线程,自旋等待或膨胀为重量级锁自旋锁:
轻量级锁失败后,JVM会让当前线程"自旋"等待(循环尝试获取锁),而不是立即阻塞。
while (尝试获取锁) {
if (成功) break;
自旋次数++;
if (自旋次数达到阈值) {
膨胀为重量级锁;
break;
}
}自旋的好处:避免线程阻塞的开销,适合锁被占用时间很短的情况。
自旋的坏处:如果锁被占用时间长,自旋会浪费CPU。
JVM自适应自旋:根据历史自旋成功率动态调整自旋次数。
(3)重量级锁
原理:使用Monitor,线程真正阻塞等待。
适用场景:竞争激烈,锁被占用时间长。
膨胀触发条件:
自旋失败次数达到阈值
多个线程同时竞争
膨胀过程:
1. 创建Monitor对象
2. 将Mark Word替换为Monitor指针
3. 竞争失败的线程进入Monitor的Entry Set阻塞2.5 锁降级
锁可以从低级升级到高级,但不会从高级降级到低级。
原因:锁降级需要全局安全点,成本高;且一旦竞争激烈,后续大概率还是竞争激烈,降级意义不大。
唯一的例外:GC过程中的锁降级(SafePoint期间)。
2.6 synchronized的优化历程

锁消除:
JVM检测到不可能被共享的对象,自动消除其同步锁。
public String concat(String s1, String s2) {
StringBuffer sb = new StringBuffer(); // sb是局部变量,不会被共享
sb.append(s1); // append是synchronized方法
sb.append(s2);
return sb.toString();
}
// JVM可以消除sb的锁,因为sb不会被其他线程访问锁粗化:
多次连续加锁解锁,JVM合并为一次加锁解锁。
for (int i = 0; i < 100; i++) {
synchronized (lock) {
// 临界区
}
}
// 优化为:
synchronized (lock) {
for (int i = 0; i < 100; i++) {
// 临界区
}
}三、Lock深入解析
3.1 Lock接口
synchronized是隐式锁(JVM管理),Lock是显式锁(代码控制)。
public interface Lock {
void lock(); // 获取锁
void lockInterruptibly(); // 可响应中断地获取锁
boolean tryLock(); // 尝试获取锁(立即返回)
boolean tryLock(long time, TimeUnit unit); // 尝试获取锁(超时返回)
void unlock(); // 释放锁
Condition newCondition(); // 创建条件变量
}核心优势:
可中断:等待锁时可响应中断
可超时:尝试获取锁有超时机制
公平性选择:可指定公平或非公平
条件变量:支持多个等待队列
3.2 ReentrantLock详解
ReentrantLock是最常用的Lock实现,基于AQS(AbstractQueuedSynchronizer)。
基本用法:
public class Counter {
private final Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 必须在finally中释放锁
}
}
// 可中断获取
public void incrementInterruptibly() throws InterruptedException {
lock.lockInterruptibly();
try {
count++;
} finally {
lock.unlock();
}
}
// 尝试获取(非阻塞)
public boolean tryIncrement() {
if (lock.tryLock()) {
try {
count++;
return true;
} finally {
lock.unlock();
}
}
return false; // 获取锁失败
}
// 尝试获取(超时)
public boolean tryIncrementTimeout() throws InterruptedException {
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
count++;
return true;
} finally {
lock.unlock();
}
}
return false; // 超时未获取到锁
}
}3.3 AQS核心原理
AQS是Java并发包的核心框架,ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch等都基于AQS。
AQS结构:
public abstract class AbstractQueuedSynchronizer {
// 核心状态变量
private volatile int state; // 同步状态(0=未锁定,>0=锁定次数)
// 等待队列
private transient volatile Node head; // 队列头
private transient volatile Node tail; // 队列尾
// 队列节点
static final class Node {
volatile Thread thread; // 等待的线程
volatile int waitStatus; // 等待状态
volatile Node prev; // 前驱节点
volatile Node next; // 后继节点
Node nextWaiter; // 条件队列中的下一个节点
}
}核心思想:
用
state表示锁状态用CAS修改
state获取锁获取失败的线程进入CLH队列等待
用LockSupport.park()阻塞线程,LockSupport.unpark()唤醒线程
获取锁流程:

公平锁与非公平锁:
// 非公平锁(默认)
public ReentrantLock() {
sync = new NonfairSync();
}
// 公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}非公平锁获取:
final void lock() {
// 直接尝试CAS获取,不检查队列
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // CAS失败,进入获取流程
}公平锁获取:
final void lock() {
acquire(1); // 不直接CAS,先检查队列
}
protected final boolean tryAcquire(int acquires) {
// 检查是否有前驱节点等待
if (hasQueuedPredecessors())
return false; // 有等待的线程,不获取
// 无等待线程,尝试CAS
return compareAndSetState(0, acquires);
}对比:
非公平锁:可能插队,吞吐量高,但可能导致某些线程长时间等待
公平锁:按顺序获取,公平性保证,但吞吐量略低
3.4 Condition条件变量
Condition是Lock的高级功能,类似synchronized的wait/notify,但更灵活。
public class BoundedQueue<T> {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final Object[] items;
private int putIndex, takeIndex, count;
public BoundedQueue(int capacity) {
items = new Object[capacity];
}
public void put(T item) throws InterruptedException {
lock.lock();
try {
while (count == items.length) {
notFull.await(); // 队列满,等待
}
items[putIndex] = item;
putIndex = (putIndex + 1) % items.length;
count++;
notEmpty.signal(); // 通知消费者
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await(); // 队列空,等待
}
T item = (T) items[takeIndex];
items[takeIndex] = null;
takeIndex = (takeIndex + 1) % items.length;
count--;
notFull.signal(); // 通知生产者
return item;
} finally {
lock.unlock();
}
}
}Condition vs wait/notify:

3.5 ReentrantReadWriteLock
读写锁:读读共享、读写互斥、写写互斥。
public class Cache {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
private Map<String, Object> cache = new HashMap<>();
public Object get(String key) {
readLock.lock();
try {
return cache.get(key);
} finally {
readLock.unlock();
}
}
public void put(String key, Object value) {
writeLock.lock();
try {
cache.put(key, value);
} finally {
writeLock.unlock();
}
}
public void clear() {
writeLock.lock();
try {
cache.clear();
} finally {
writeLock.unlock();
}
}
}原理:
AQS的state分为两部分:
高16位:共享锁(读锁)计数
低16位:独占锁(写锁)计数
state = (sharedCount << 16) | exclusiveCount
读锁:state & 0xFFFF0000
写锁:state & 0x0000FFFF锁降级:
写锁可以降级为读锁(持有写锁时获取读锁),但不能升级(持有读锁时获取写锁会死锁)。
public void processData() {
writeLock.lock();
try {
// 更新数据
updateData();
// 降级为读锁,继续读取数据(其他线程不能写)
readLock.lock();
try {
writeLock.unlock(); // 先释放写锁(在持有读锁的情况下)
// 现在持有读锁,可以安全读取
readProcessedData();
} finally {
readLock.unlock();
}
} finally {
// 注意:此时写锁已释放,这段代码实际不会执行
}
}3.6 StampedLock
JDK 8引入的乐观读写锁,性能更好但使用复杂。
public class Point {
private final StampedLock sl = new StampedLock();
private double x, y;
// 写锁
public void move(double deltaX, double deltaY) {
long stamp = sl.writeLock(); // 获取写锁戳
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp); // 释放写锁(需要戳)
}
}
// 读锁
public double distanceFromOrigin() {
long stamp = sl.readLock(); // 获取读锁戳
try {
return Math.sqrt(x * x + y * y);
} finally {
sl.unlockRead(stamp);
}
}
// 乐观读(无锁读取,验证版本)
public double optimisticRead() {
long stamp = sl.tryOptimisticRead(); // 获取乐观读戳
double currentX = x, currentY = y;
// 验证期间是否有写操作
if (!sl.validate(stamp)) {
// 有写操作,乐观读失败,升级为读锁
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
}特点:
乐观读:不加锁读取,验证期间是否有写操作
性能更好:读操作大部分情况下不需要获取锁
使用复杂:需要手动验证、升级锁
四、synchronized与Lock对比
4.1 功能对比

4.2 性能对比
JDK 1.6之前:Lock性能优于synchronized(synchronized只有重量级锁)
JDK 1.6之后:synchronized优化后,两者性能差距缩小
实测对比(高并发场景):

4.3 使用选择
推荐synchronized的场景:
简单同步需求
不需要高级功能(中断、超时、公平锁)
代码可读性优先(自动释放锁)
推荐Lock的场景:
需要中断获取锁
需要超时机制
需要公平锁
需要多个条件变量
需要读写锁
复杂并发控制
五、锁实战:问题排查与优化
5.1 死锁问题
经典死锁场景:
public class DeadlockDemo {
private final Object lockA = new Object();
private final Object lockB = new Object();
public void methodA() {
synchronized (lockA) {
System.out.println("Thread A holds lockA");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lockB) {
System.out.println("Thread A holds lockA and lockB");
}
}
}
public void methodB() {
synchronized (lockB) {
System.out.println("Thread B holds lockB");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lockA) {
System.out.println("Thread B holds lockB and lockA");
}
}
}
public static void main(String[] args) {
DeadlockDemo demo = new DeadlockDemo();
new Thread(demo::methodA).start();
new Thread(demo::methodB).start();
}
}死锁排查:
# 1. 查看线程栈
jstack <pid>
# 输出:
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x... (object 0x..., a java.lang.Object)
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x... (object 0x..., a java.lang.Object)
which is held by "Thread-1"解决方案:
// 1. 统一加锁顺序
public void methodA() {
synchronized (lockA) {
synchronized (lockB) {
// 临界区
}
}
}
public void methodB() {
synchronized (lockA) { // 先获取lockA
synchronized (lockB) {
// 临界区
}
}
}
// 2. 使用Lock的超时机制
public void methodA() throws InterruptedException {
if (lockA.tryLock(1, TimeUnit.SECONDS)) {
try {
if (lockB.tryLock(1, TimeUnit.SECONDS)) {
try {
// 临界区
} finally {
lockB.unlock();
}
}
} finally {
lockA.unlock();
}
}
}
// 3. 使用Lock的一致性哈希(避免嵌套)
// 按对象哈希值顺序获取锁5.2 锁竞争问题
现象:高并发时系统响应变慢,CPU利用率低。
诊断:
# 查看线程状态
jstack <pid> | grep BLOCKED
# 输出:
"Thread-10" #10 prio=5 os_prio=0 tid=0x... nid=0x... waiting for monitor entry [0x...]
waiting to lock <0x...> (a java.lang.Object)
which is held by "Thread-0"大量BLOCKED状态线程 = 锁竞争严重。
解决方案:
1. 减小锁粒度
// 原方案:全局锁
public class Cache {
private final Object lock = new Object();
private Map<String, Object> data = new HashMap<>();
public Object get(String key) {
synchronized (lock) {
return data.get(key);
}
}
}
// 优化:分段锁
public class Cache {
private final Object[] locks = new Object[16];
private final Map<String, Object>[] segments = new Map[16];
public Cache() {
for (int i = 0; i < 16; i++) {
locks[i] = new Object();
segments[i] = new HashMap<>();
}
}
private int segmentIndex(String key) {
return key.hashCode() % 16;
}
public Object get(String key) {
int index = segmentIndex(key);
synchronized (locks[index]) {
return segments[index].get(key);
}
}
}2. 使用读写锁
读多写少场景,使用ReadWriteLock:
public class Cache {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private Map<String, Object> data = new HashMap<>();
public Object get(String key) {
rwLock.readLock().lock();
try {
return data.get(key);
} finally {
rwLock.readLock().unlock();
}
}
public void put(String key, Object value) {
rwLock.writeLock().lock();
try {
data.put(key, value);
} finally {
rwLock.writeLock().unlock();
}
}
}3. 使用并发容器
// ConcurrentHashMap:分段锁(JDK 7)或CAS+synchronized(JDK 8+)
ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();
// 无锁操作
cache.put(key, value); // 内部已优化
cache.get(key);4. 使用无锁方案
// AtomicLong:CAS无锁
AtomicLong counter = new AtomicLong(0);
counter.incrementAndGet(); // 无阻塞
// LongAdder:分段累加(高并发更好)
LongAdder adder = new LongAdder();
adder.increment();5.3 锁优化实战案例
案例1:订单系统锁优化
背景:订单创建流程,用synchronized保护整个流程,高峰期响应慢。
问题分析:
// 原代码
public Order createOrder(OrderRequest request) {
synchronized (this) {
// 1. 检查库存(耗时100ms)
checkInventory(request);
// 2. 计算价格(耗时50ms)
calculatePrice(request);
// 3. 创建订单(耗时50ms)
Order order = saveOrder(request);
// 4. 发送通知(耗时100ms)
sendNotification(order);
return order;
}
}锁住了整个流程,包括不需要同步的步骤。
优化方案:
// 只锁需要同步的部分
public Order createOrder(OrderRequest request) {
// 1. 检查库存(可并行,用乐观锁)
Inventory inventory = checkInventoryOptimistic(request);
// 2. 计算价格(无需锁)
Price price = calculatePrice(request);
// 3. 创建订单(需要锁)
Order order;
synchronized (orderLock) {
order = saveOrder(request, inventory, price);
}
// 4. 发送通知(异步,不阻塞)
notificationService.sendAsync(order);
return order;
}
// 库存乐观锁
public Inventory checkInventoryOptimistic(OrderRequest request) {
while (true) {
Inventory inv = inventoryDao.get(request.getProductId());
if (inv.getStock() >= request.getQuantity()) {
// CAS更新库存
boolean success = inventoryDao.casUpdate(
inv.getId(),
inv.getVersion(),
inv.getStock() - request.getQuantity()
);
if (success) {
return inv;
}
} else {
throw new InsufficientStockException();
}
}
}效果:响应时间从300ms降到150ms,吞吐量提升50%。
案例2:缓存系统锁优化
背景:缓存系统,读多写少,synchronized导致读取性能下降。
优化:
// 原方案
public class Cache {
private final Object lock = new Object();
private Map<String, Object> cache = new HashMap<>();
public Object get(String key) {
synchronized (lock) {
return cache.get(key);
}
}
public void put(String key, Object value) {
synchronized (lock) {
cache.put(key, value);
}
}
}
// 优化1:读写锁
public class Cache {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private Map<String, Object> cache = new HashMap<>();
public Object get(String key) {
rwLock.readLock().lock();
try {
return cache.get(key);
} finally {
rwLock.readLock().unlock();
}
}
}
// 优化2:StampedLock(性能更好)
public class Cache {
private final StampedLock sl = new StampedLock();
private Map<String, Object> cache = new HashMap<>();
public Object get(String key) {
long stamp = sl.tryOptimisticRead();
Object value = cache.get(key);
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
value = cache.get(key);
} finally {
sl.unlockRead(stamp);
}
}
return value;
}
}
// 优化3:ConcurrentHashMap(最简单)
public class Cache {
private ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();
public Object get(String key) {
return cache.get(key); // 无锁读取
}
public void put(String key, Object value) {
cache.put(key, value); // 内部优化
}
}效果:读取性能提升10倍(ConcurrentHashMap方案)。
5.4 锁使用最佳实践
原则1:锁范围最小化
// 不推荐
public void method() {
synchronized (lock) {
// 大量代码,大部分不需要同步
prepareData(); // 无需锁
processData(); // 需要锁
saveResult(); // 需要锁
sendNotification(); // 无需锁
}
}
// 推荐
public void method() {
prepareData();
synchronized (lock) {
processData();
saveResult();
}
sendNotification();
}原则2:避免嵌套锁
// 不推荐(可能死锁)
public void methodA() {
synchronized (lockA) {
synchronized (lockB) {
// 临界区
}
}
}
// 推荐(单一锁)
public void methodA() {
synchronized (lockA) {
// 用其他方式实现功能
}
}原则3:用并发容器代替手动加锁
// 不推荐
Map<String, Integer> map = new HashMap<>();
synchronized (lock) {
map.put(key, value);
}
// 推荐
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put(key, value); // 内部已优化原则4:正确释放锁
// 不推荐(异常时锁不释放)
public void method() {
lock.lock();
// 可能抛异常
doSomething();
lock.unlock(); // 异常后不会执行
}
// 推荐
public void method() {
lock.lock();
try {
doSomething();
} finally {
lock.unlock(); // 总是释放
}
}原则5:wait/notify在循环中使用
// 不推荐(虚假唤醒)
synchronized (lock) {
if (condition) {
lock.wait();
}
// 条件可能已改变
}
// 推荐
synchronized (lock) {
while (!condition) {
lock.wait();
}
// 条件一定满足
}六、常见问题
Q1:synchronized和Lock怎么选?
简单场景用synchronized:自动释放锁、代码简洁。 高级需求用Lock:中断、超时、公平锁、多条件变量、读写锁。
Q2:偏向锁为什么被废弃?
偏向锁复杂度高、维护困难,且现代应用多线程竞争普遍,收益有限。JDK 15默认禁用。
Q3:什么情况锁会膨胀?
竞争激烈时:自旋失败次数达到阈值、多个线程同时竞争。从轻量级锁膨胀到重量级锁。
Q4:CAS是什么?
Compare And Swap,无锁更新:
if (当前值 == 期望值) {
更新为新值;
return 成功;
} else {
return 失败;
}是乐观锁的核心,AQS依赖CAS。
Q5:如何判断是否需要加锁?
看是否有共享变量被多个线程访问:
局部变量:不需要锁(线程私有)
ThreadLocal:不需要锁(线程私有)
共享变量:需要锁或使用并发容器
Q6:锁竞争严重怎么排查?
jstack <pid> | grep BLOCKED # 查看阻塞线程
jstack <pid> # 查看完整线程栈大量BLOCKED线程 = 锁竞争严重。
七、总结
Java锁机制是并发编程的核心,理解它需要从三个层面:
原理层面:
synchronized:Monitor机制、对象头、锁升级
Lock:CAS、AQS、等待队列
演进层面:
synchronized:从重量级锁到偏向锁、轻量级锁、自适应自旋
Lock:从简单互斥锁到可重入锁、读写锁、StampedLock
实战层面:
选择合适的锁类型
优化锁的范围和粒度
排查和解决锁相关问题
实际项目中,锁使用的关键是:
确有必要才加锁(避免过度同步)
锁范围最小化
用并发容器代替手动锁
监控锁竞争,及时优化
锁不是银弹,合理使用才能既保证安全又保证性能。
参考资料:
《深入理解Java虚拟机(周志明)》
《Java并发编程的艺术》