synchronized修饰实例方法锁this,修饰静态方法锁Class对象;同步块可指定任意锁对象,粒度更细、性能更好,且兼具原子性、可见性与有序性。

同步方法锁的是什么对象
Java中用 synchronized 修饰实例方法时,锁的是当前实例(this);修饰静态方法时,锁的是该类的 Class 对象(如 MyClass.class)。这意味着:多个线程调用同一个对象的不同同步实例方法,会互斥;但调用不同对象的同步实例方法,不互斥。
常见错误是以为“加了 synchronized 就全局串行”,结果发现并发没被挡住——其实只是锁粒度不够或锁对象不对。
- 实例方法 → 锁
this,适用于保护该对象内部状态 - 静态方法 → 锁
MyClass.class,适用于保护类级别共享资源(如静态计数器、单例初始化) - 若方法体里只操作局部变量,加
synchronized毫无意义,纯属性能浪费
同步块能精确控制锁范围和锁对象
同步块(synchronized(obj) { ... })让你明确指定锁对象,并且只包裹真正需要同步的代码段。相比同步方法,它更灵活、更轻量,也更容易避免锁升级或死锁。
典型使用场景:你只想保护某个共享集合的读写,但方法里还有大量IO或计算逻辑——这些不该被锁住。
立即学习“Java免费学习笔记(深入)”;
网趣网上购物系统支持PC电脑版+手机版+APP,数据一站式更新,支持微信支付与支付宝支付接口,是专业的网上商城系统,网趣商城系统支持淘宝数据包导入,实现与淘宝同步更新!支持上传图片水印设置、图片批量上传功能,同时支持订单二次编辑以及多级分类隐藏等实用功能,新版增加商品大图浏览与列表显示功能,使分类浏览更方便,支持最新的支付宝即时到帐接口。
- 锁对象可以是任意非空引用,推荐用私有 final 对象(如
private final Object lock = new Object();),避免外部误操作 - 不要用
this或getClass()做同步块锁,容易被子类或外部代码干扰 - 避免在同步块内调用外部可重入方法(比如回调、第三方库方法),可能引发死锁或不可预知阻塞
private final Object lock = new Object(); private Listitems = new ArrayList<>(); public void addItem(String item) { // 只锁集合操作,不锁日志、校验等非共享逻辑 synchronized (lock) { items.add(item); } log.info("Added: {}", item); // 这行不在锁内 }
性能与可维护性差异在哪
同步方法隐式锁整个方法体,哪怕只有2行代码访问共享变量,其余几十行也会被拖慢;同步块则把锁收缩到最小必要范围,减少线程等待时间。在高并发场景下,这个差异会被放大。
但同步块也带来额外负担:你需要自己管理锁对象生命周期、确保所有访问路径都用同一把锁、避免漏锁或重复锁。
- 简单工具类、低并发场景,同步方法写起来快、不易出错
- 服务核心逻辑、共享缓存、高频更新状态,优先用同步块 + 明确锁对象
- 注意:JVM对同步方法有一定优化(如锁消除、偏向锁),但这些依赖运行时逃逸分析,不可控;同步块的锁行为更确定
别忽略锁的可见性语义
无论是同步方法还是同步块,它们不仅保证互斥执行,还保证进入/退出时刷新主内存中的变量值——即提供 happens-before 关系。这点常被忽视,导致有人用 volatile 替代 synchronized,结果在复合操作(如先读再写)上出错。
-
volatile只保证单个读/写可见性,不保证原子性 -
synchronized同时提供原子性 + 可见性 + 有序性 - 比如
counter++是读-改-写三步,必须用同步(方法或块),volatile无法替代
最易被绕过的点:你以为只读不写就不用同步,但若其他线程正在写,而你读到的是旧值,问题就藏在“可见性”里。









