多线程真的难啊 家人萌
2.1 讲一下synchronized关键字的底层原理?
是什么->修饰谁->底层->过程
synchronized 是 JVM 层面的同步机制,主要用来保证同一时间只有一个线程进入临界区。
如果修饰代码块,编译后会生成 `monitorenter` 和 `monitorexit` 指令;如果修饰方法,会在方法上加同步标记。
它底层和对象头里的 Mark Word 以及 Monitor 有关,每个 Java 对象都可以作为锁对象。线程抢锁成功后,会成为这个 Monitor 的 Owner;其他线程竞争失败会进入 EntryList 阻塞;如果线程调用了 `wait()`,会进入 WaitSet。锁释放后,EntryList 里的线程会重新竞争锁,这个竞争不是公平的。
另外 synchronized 是可重入锁,JDK 1.6 之后也做了锁优化,比如偏向锁、轻量级锁、重量级锁,只有竞争比较激烈时才会膨胀成重量级锁,这时候阻塞和唤醒线程的成本会比较高。
2.2 synchronized 的锁升级过程是什么?偏向锁、轻量级锁、重量级锁分别适合什么场景?
synchronized 不是一上来就使用重量级锁,JVM 会根据竞争情况做锁优化。
早期常说的锁状态包括偏向锁、轻量级锁和重量级锁。偏向锁适合长时间只有一个线程反复进入同步块的场景,它会把线程 ID 记录到对象头 Mark Word 里,后续同一个线程再进入时,基本只需要判断是不是自己。
如果有其他线程也来使用这把锁,偏向锁可能会被撤销,进入轻量级锁。轻量级锁适合线程交替执行、竞争不激烈的场景,底层主要通过 CAS 修改对象头 Mark Word 来尝试加锁。
如果竞争变得激烈,比如 CAS 一直失败,线程需要阻塞,就会膨胀成重量级锁。重量级锁底层依赖 Monitor,涉及线程阻塞和唤醒,成本会更高。
另外偏向锁要注意版本问题,JDK 15 之后默认已经禁用了,所以面试里可以作为历史优化机制来说,但不能简单认为现在默认一定有偏向锁。
2.3 你谈谈 JMM(Java 内存模型)
JMM,也就是 Java 内存模型,它主要是用来规范多线程环境下共享变量的读写规则。
可以简单理解为,共享变量存放在主内存中,每个线程有自己的工作内存,线程操作变量时,通常是操作自己工作内存里的副本,线程之间不能直接访问对方的工作内存,只能通过主内存来完成数据同步。
但这个模型不是单纯讲存储位置,它真正要解决的是并发里的三个问题:可见性、有序性和原子性。比如一个线程改了变量,另一个线程什么时候能看到,这是可见性;编译器和 CPU 可能会做指令重排序,这是有序性;像 `i++` 这种复合操作不是原子的,就涉及原子性问题。
Java 里像 `volatile`、`synchronized` 以及 happens-before 规则,都是围绕 JMM 来保证这些并发语义的。比如 volatile 能保证可见性和禁止指令重排序,synchronized 既能保证互斥,也能保证释放锁前的修改对后续加锁线程可见。

196

被折叠的 条评论
为什么被折叠?



