无锁 → 偏向 → 轻量级(自旋/CAS) → 重量级(monitor) — 锁的全部状态都在对象头的 Mark Word 里
synchronized 的聪明之处是"按竞争激烈程度付费": 没人抢时近乎免费(Mark Word 记个标记), 竞争短暂时 CAS+自旋硬扛(轻量级), 打持久战才动用 OS 互斥量(重量级)。这条升级路径的全部状态都写在对象头 Mark Word 里 — 理解了它, "synchronized 到底慢不慢"这个争论你就有标准答案: 无竞争时几乎免费, 高竞争时真正要优化的是锁粒度, 而不是换个锁。
synchronized (obj) { ... } // monitorenter/monitorexit obj.wait(); obj.notify(); // 直接操作 WaitSet/EntryList // 关键: owner + EntryList + WaitSet 是堆外 C++ 结构
java -XX:+PrintFlagsFinal -version | grep -i Bias # JDK 8: UseBiasedLocking=true; JDK 15+ 默认禁用(JEP 374) # 关键: 偏向撤销要全局 safepoint, 成本盖过收益
synchronized (lock) { i++; } // 临界区极短: 自旋几 μs 就能赢 // 关键: 自适应自旋按历史成功率定次数, 上次赢这次多转
synchronized (sb) { sb.append(x); } // 局部 sb 不逃逸 → JIT 删锁 for (...) { synchronized (o) { ... } } // 粗化: 合并成循环外一把
synchronized (lock) { // ① 不持锁 → IllegalMonitorStateException while (!ready) lock.wait(); // ③ while 重检; ② wait 已释放锁 }
// AQS = volatile state + CAS + CLH 队列挂起等待线程 acquire(1); // ReentrantLock: state 累加 = 重入计数 // Semaphore / CountDownLatch 只是换 state 语义的插件
synchronized void a() { b(); } // 同线程嵌套: 直接进, 不死锁 synchronized void b() { ... } // 关键: state 计数, 出一次减一次
风控规则计算, 100ms 一把大锁 → 全局串行; 按 userId 分段后并发恢复:
// 差: 全局一把锁, 无关用户互相排队 synchronized (rulesCache) { compute(user); } // 好: 分段锁 — 不同段并行, 同段才互斥 private static final Object[] LOCKS = new Object[64]; Object lock = LOCKS[Math.floorMod(userId.hashCode(), LOCKS.length)]; synchronized (lock) { compute(user); } // 更省心: ConcurrentHashMap<K,V>.computeIfAbsent() 内部按桶加锁, 通常不用手写
jstack <pid> | grep -A 20 "Found one Java-level deadlock" # 输出直接给出互相等待的线程与代码行 → 调整加锁顺序(全局排序拿锁)修复 # 预防: 团队规约"多把锁必须按固定顺序获取"; 带超时的 tryLock 打破循环等待
System.out.println(ClassLayout.parseInstance(obj).toPrintable()); // 无锁: ... 0x01 (可看到 hashcode 位) synchronized (obj) { System.out.println(ClassLayout.parseInstance(obj).toPrintable()); // 轻量级: ... 0x00 + 栈 Lock Record 指针 (JDK 15+ 无偏向, 直接轻量级) }
三种方案覆盖 99% 场景, 按竞争强度升级:
// 1. 低频读: synchronized 一步到位 (JIT 锁优化足够) synchronized (map) { return map.get(k); } # 2. 读多写少: ReadWriteLock 放行并发读 rw.readLock().lock(); try { return map.get(k); } finally { rw.readLock().unlock(); } # 3. 读极多: StampedLock 乐观读 — 读时零锁 long stamp = sl.tryOptimisticRead(); // 不加锁, 拿版本号 V v = data; if (!sl.validate(stamp)) { // 读期间有写? 才升级悲观读 stamp = sl.readLock(); try { v = data; } finally { sl.unlockRead(stamp); } }
转账要同时锁两个账户, 顺序不定就有死锁风险 — 有界等待+重试是标准解:
public boolean transfer(Account from, Account to, long amt) throws InterruptedException { while (true) { if (from.lock.tryLock(1, SECONDS)) { try { if (to.lock.tryLock(1, SECONDS)) { try { from.debit(amt); to.credit(amt); return true; } finally { to.lock.unlock(); } } } finally { from.lock.unlock(); } } backoff(); // 没拿到就退避重来, 死锁不可能形成 } }
生产者消费者自己写 wait/notify 是历史课 — BlockingQueue 三行搞定且无唤醒坑:
BlockingQueue<Order> buffer = new ArrayBlockingQueue<>(500); # 生产者: 队满自动阻塞 (背压) buffer.put(order); # 消费者: 队空自动等待, 无需手动 wait/notifyAll/while 重检 Order o = buffer.take(); process(o);
无锁更新带上限退避, 避免高竞争下自旋风暴:
public static <T> boolean casRetry(AtomicReference<T> ref, UnaryOperator<T> update, int maxRetries) { for (int i = 0; i < maxRetries; i++) { T cur = ref.get(); T next = update.apply(cur); if (ref.compareAndSet(cur, next)) return true; Thread.onSpinWait(); // JDK9+: 提示 CPU 让出执行资源 } return false; // 重试耗尽: 调用方走慢路径(加锁/MQ) }
synchronized 只锁进程内 — 多实例部署时同 key 并发照样穿透:
// 单实例够用: JVM 锁 (快, 无外部依赖) synchronized (userId.intern()) { grantOnce(uid); } // 注意: intern 词汇有限才安全 # 多实例必须: 分布式锁 + 数据库唯一约束双保险 if (redisLock.tryLock("grant:" + uid, 5s)) { try { dao.insertUnique(uid); } // 最终防线是唯一索引 catch (DuplicateKeyException e) { // 锁失效也不脏 } finally { redisLock.unlock(); } }
BLOCKED 线程数是锁健康度的直接指标:
// 常态巡检: BLOCKED 数量与等待目标 jstack <pid> | grep -c "java.lang.Thread.State: BLOCKED" # 告警口径: BLOCKED > 核数 且持续 1 分钟 → P2 告警 # 定位: jstack 输出里 "waiting to lock <0x...>" 对应 "locked <0x...>" 的线程栈 # 也可用 arthas: thread --state BLOCKED 一屏看全
有界缓冲区"满等空、空等满"两个条件, 一个 lock 配两个 condition:
private final ReentrantLock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); public void put(T x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); // 满了等"非满" items[putIdx] = x; if (++putIdx == items.length) putIdx = 0; count++; notEmpty.signal(); // 只叫醒等"非空"的 } finally { lock.unlock(); } }
synchronized("lock"): 字符串常量池全局共享, 不同模块可能"锁到同一把"或"以为同锁实则不同"; Integer 缓存 -128~127 同理。正解: private static final Object LOCK = new Object()。 synchronized ("lock") { ... } // 错: 常量池全局共享, 锁到同一把 private static final Object LOCK = new Object(); // 对
synchronized (this) { ... } // 错: 外部也能锁你的实例 private final Object lock = new Object(); // 对: 锁权收进类内
while (!condition) lock.wait();。 if (!cond) lock.wait(); // 错: 虚假唤醒+条件再变 while (!cond) lock.wait(); // 对: 唤醒后重检条件
synchronized (lock) { rpc.call(); } // 错: 全局串行化 synchronized (lock) { snap = copy(); } // 对: 锁内只做内存操作
lock.lock(); // 错: 异常路径漏 unlock, 后续全部卡死 lock.lock(); try { ... } finally { lock.unlock(); } // 对
private static Singleton inst; // 错: 重排 → 半成品 private static volatile Singleton inst; // 对: 或静态内部类/枚举
// 错: "Lock 一定比 synchronized 快" — 无竞争时反而常更慢 lock.tryLock(1, SECONDS); // 对: 价值在超时/可中断/公平/Condition
// 错: 锁随序列化/clone 复制出第二把 → 互斥失效 private transient Object lock; // 对: transient + 重建, 拷贝走工厂
static synchronized void a() {} // 锁 Foo.class synchronized void b() {} // 锁 this — 两把锁互不互斥!
synchronized (lock) { ... } // 错: 死等, 无法响应取消 if (rlock.tryLock(3, SECONDS)) // 对: 超时/可中断
new ReentrantLock(true); // 错: 默认上公平, 吞吐明显降 new ReentrantLock(); // 对: 非公平 + 业务超时兜底
notify(); // 错: 先于 wait 执行 → 等待者睡死 ready = true; notifyAll(); // 对: 状态字段 + while 重检
// 错: "同时改 A 段和 B 段"锁两段 → 死锁风险; 不锁 → 撕裂 // 对: 跨段操作走全局锁, 或重设计分区让操作单段化
ref.compareAndSet(100, next); // 错: 100→80→100 也放行 AtomicStampedReference<Long> ref; // 对: 版本号 / DB version 列
// 错: StampedLock 非重入 — 同线程二次 readLock 可死锁 ReentrantReadWriteLock rw; // 对: 需要重入的场景用它
long s = sl.tryOptimisticRead(); ... // 乐观读不断 → 写饥饿 // 对: 限制乐观读比例或换 RWLock, 监控写等待时间
synchronized (lock) { this.hook(); } // 错: 子类再拿锁/做 IO // 对: 锁只包数据操作, 扩展点移出临界区
Collections.synchronizedMap(new HashMap<>()); // 错: 全局锁 new ConcurrentHashMap<String,Object>(); // 对: 直接 CHM
for (String k : map.keySet()) map.remove(k); // 错: 语义不可靠 map.keySet().removeIf(...); // 对: 原子方法/快照
ctx.register(this); // 错: 构造里发布 this, volatile 也救不了 // 对: 构造器绝不发布 this; 单例优先静态内部类/枚举