synchronized在异常时自动释放锁,无需手动干预,但锁对象不能为null;jvm通过monitorenter/monitorexit配对保障锁释放;唯一崩溃场景是synchronized(null);推荐用final object锁、判空校验、缩小同步范围。

synchronized 在异常情况下会自动释放锁,不需要手动干预,但必须避免锁对象为 null 导致的 NullPointerException —— 这是唯一可能在进入同步块前就崩溃的情况。
异常发生时锁一定会被释放
无论同步代码块或同步方法中是否抛出异常(包括未捕获的 RuntimeException),JVM 都会保证锁被正确释放。这是因为:
- 字节码层面,
monitorenter后必然配对生成至少一个monitorexit指令; - 异常路径也会触发对应的
monitorexit,确保锁不会永久持有; - 这一机制由 JVM 保障,开发者无需、也不应尝试在 catch 中手动解锁。
synchronized(null) 会直接崩溃
这是唯一不涉及临界区执行、却会在“进入同步块前”就失败的情形:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
-
synchronized(obj)要求obj != null,否则运行时抛出 NullPointerException; - 该检查发生在
monitorenter指令执行时,编译器不报错,IDE 也无提示; - 常见于延迟初始化、配置未加载、或外部传参未校验的场景。
推荐的安全写法
核心是让锁对象可控、非空、且粒度合理:
- 用
final Object lock = new Object();作为固定锁对象,杜绝 null 风险; - 若需动态锁对象,进入
synchronized前显式判空并提供 fallback 或日志; - 优先使用同步代码块而非同步方法,缩小锁范围,减少阻塞;
- 临界区内若需处理业务异常,用 try-catch 包裹具体逻辑,不影响锁释放流程。
不需要在 finally 里做任何锁操作
有人误以为要像 IO 流那样手动释放锁,其实完全多余:
- 锁的释放由 JVM 自动完成,与异常类型、是否捕获、finally 是否存在无关;
- 在 synchronized 块内加 finally 并不能增强锁安全性,反而可能干扰逻辑;
- 真正该在 finally 中处理的,是那些需要显式释放的资源(如数据库连接、文件句柄)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










