上一篇: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 释放}| 对比 synchronized | ReentrantLock |
|---|---|
| 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;锁范围越小越好。