synchronized 与 Lock

笔记/Java/Java基础/Java并发/synchronized 与 Lock

上一篇:JMM 与 volatile · 下一篇:Atomic 与 CAS

synchronized 三种用法#

// 1. 同步实例方法 — 锁当前实例
public synchronized void instanceMethod() {}
// 2. 同步静态方法 — 锁 Class 对象
public static synchronized void staticMethod() {}
// 3. 同步块 — 锁指定对象,范围越小越好
public void block() {
synchronized (this) {
// 临界区
}
}

同步块优于同步整个方法:锁外代码可并发执行,吞吐更高。

底层原理(简述)#

基于 monitor(监视器):字节码 monitorenter / monitorexit。每个对象关联一个 monitor;线程进入 synchronized 即尝试获取 monitor。

可重入锁#

同一线程可多次获取同一把锁(monitor 进入计数 +1)。synchronized 与 ReentrantLock 都是可重入的。

锁升级(JDK 偏向/轻量/重量)#

无竞争时偏向锁 → 有竞争升级轻量级锁(CAS 自旋)→ 竞争激烈升级为重量级锁(OS mutex)。目的:在无/少竞争时减少开销。

ReentrantLock#

ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// 临界区
} finally {
lock.unlock(); // 必须在 finally 释放
}
对比 synchronizedReentrantLock
JVM 关键字,自动释放API 锁,需手动 unlock
不可中断等待lockInterruptibly
非公平默认可选公平锁
单一条件多个 Condition

Thread.holdsLock(obj) 可检测当前线程是否持有锁。

ReadWriteLock#

读读不互斥,读写、写写互斥,读多写少场景提升吞吐:

ReadWriteLock rw = new ReentrantReadWriteLock();
rw.readLock().lock();
try { /* 读 */ } finally { rw.readLock().unlock(); }

死锁、活锁、饥饿#

现象说明
死锁互相持有并等待对方锁,需外力或超时打破
活锁互相谦让释放资源,都不前进
饥饿低优先级或长期拿不到锁/CPU

死锁四条件:互斥、占有且等待、不可抢占、循环等待。避免:固定加锁顺序、缩小锁范围、tryLock 超时。

自旋锁#

线程不挂起,循环 CAS 尝试获锁;适合锁持有时间极短;过长自旋浪费 CPU(JDK 自适应自旋)。

线程安全 Servlet?#

Servlet 实例可能被多线程共享,不要在成员变量存请求状态;局部变量线程安全。有共享状态需同步或使用 ThreadLocal(见 ThreadLocal 笔记)。

一句话总结#

默认用 synchronized 简单场景;需要 tryLock、公平、多条件时用 ReentrantLock;读多写少用 ReadWriteLock;锁范围越小越好。

文章目录

文章目录