一、开头先说人话

兄弟们,synchronized 这东西,初学者看它像保安,面试官看它像祖传考点,项目里看它又像个老实巴交但关键时刻真能顶的哥们。

它干的事其实特别朴素:

同一时刻,只让一个线程进来干活。

就像烧烤摊只有一把秘制刷子,谁先拿到谁先刷,别的兄弟先排队,别都挤上来把羊肉刷成拖把。


二、synchronized 的底层原理是什么?

底层核心就一个词:monitor(监视器)

你可以把 monitor 理解成对象门口的门禁。线程想进同步代码块,先刷门禁;刷成功就进去,刷不成功就等着。

要注意一个细节:

  • 同步代码块,字节码里通常会看到 monitorenter 和 monitorexit
  • 同步方法,不一定直接编译成这两个指令,它更多是依赖方法的 ACC_SYNCHRONIZED 标志,由 JVM 在调用和返回时隐式加锁、释放锁

图 1:synchronized 底层执行流程

一句话翻译:

synchronized 不是给代码上锁,而是线程去竞争“某个对象关联的监视器”。


三、synchronized 锁的到底是什么?

这块特别容易被问,也特别容易答成“锁代码”。
不对,锁的不是代码,锁的是对象

具体分 3 种:

  • 修饰普通方法:锁的是当前对象,也就是 this
  • 修饰静态方法:锁的是当前类对应的 Class 对象
  • 修饰同步代码块:锁的是你括号里写的那个对象

图 2:三种锁对象方式

所以别再说“这个 synchronized 锁住了这几行代码”。
更准确的说法应该是:

这几行代码执行前,线程先去抢某个对象的 monitor。


四、什么是可重入锁?

可重入,说白了就是:

同一个线程已经拿到这把锁了,再进一次,不会把自己堵死。

这很像你已经是烧烤摊老板了,进后厨不用再跟自己打申请。
不然你刚进厨房,又得敲门问“老板我能不能进”,结果老板就是你自己,那就很尴尬了。

一个最小代码例子

public class SyncDemo {
    private int count = 0;

    public synchronized void add() {
        print(); // 我已经拿到 this 的锁了,再进一次也没事,这就叫可重入
        count++;
        System.out.println("count = " + count);
    }

    private synchronized void print() {
        System.out.println(Thread.currentThread().getName() + " 正在干活");
    }

    public static void main(String[] args) {
        SyncDemo demo = new SyncDemo();
        new Thread(demo::add, "t1").start();
    }
}

这段代码能正常跑,不会把自己锁死,就是因为 synchronized 是可重入锁。

图 3:可重入的意思

五、synchronized 和 ReentrantLock 有什么区别?

这俩都能加锁,也都可重入,但性格不一样。

如果你只是普通同步场景,synchronized 就够用了,简单、省心、不容易忘记解锁。

如果你需要这些高级玩法:

  • 公平锁
  • 可中断锁
  • 超时获取锁
  • 多条件队列

那就让 ReentrantLock 上场。

一句话总结:

日常开发大多数场景用 synchronized,复杂控制再上 ReentrantLock。


六、锁升级过程是什么?

这块是八股高频题,但也最容易背成旧版本说明书。

经典面试口径一般会说:

  • 偏向锁
  • 轻量级锁
  • 重量级锁

意思是 JVM 会根据竞争激烈程度,尽量先用轻一点的方式搞定;竞争越来越激烈,再慢慢“膨胀”成更重的锁。

图 4:经典锁状态演进

但这里必须补一句版本说明,别把老知识背成永久真理:

在 HotSpot 里,偏向锁从 JDK 15 开始已经默认禁用并被弃用。

所以今天你再讲锁优化,更稳妥的表达是:

  • synchronized 确实经历过多种锁优化设计
  • “偏向锁 -> 轻量级锁 -> 重量级锁”是经典理解模型
  • 但不同 JDK 版本实现细节并不完全一样,不能一把梭哈到所有版本

七、为什么以前总说 synchronized 很重,现在又说它没那么慢了?

因为早年它确实容易走到操作系统层面的互斥量,线程挂起、唤醒开销大,像你为了烤两串羊肉,专门把整条街交通都管制了,成本当然高。

后来 JVM 做了不少优化,比如:

  • 更轻量的竞争路径
  • 自旋等手段减少用户态/内核态切换
  • 锁状态按竞争情况逐步升级

所以现在再说 synchronized,不能张嘴就是“它性能差”。
更靠谱的说法是:

它在低竞争或一般竞争场景下,已经没有当年那么笨重了;性能要看具体并发场景。


八、整篇文章你最该记住的 5 句话

  1. synchronized 的底层核心是 monitor
  2. 同步代码块常见 monitorenter / monitorexit,同步方法常见 ACC_SYNCHRONIZED
  3. 它锁的不是代码,而是对象的监视器
  4. 它是可重入锁
  5. 现代 JVM 对它做过很多优化,但锁实现细节会随 JDK 版本变化

一句话总结

synchronized 本质上就是线程去抢对象 monitor 的门禁系统,锁的是对象,支持可重入,JVM 还会根据竞争情况尽量把这把锁用得更聪明。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐