Java面试总结
1.synchronized 通过监视器锁来实现线程同步
2.每个 Java 对象都有一个监视器锁
3.线程在获取了对象的监视器锁后,可以执行被修饰的代码
4.线程在释放了对象的监视器锁后,其他线程可以尝试获取监视器锁
5.Monitor 的结构:
1.Owner:当前持有锁的线程
2.EntryList:等待获取锁的线程队列
3.WaitSet:调用 wait() 方法后进入等待状态的线程队列
6.锁的状态:
1.无锁状态:对象未被任何线程锁定
2.偏向锁:只有一个线程访问锁时,JVM 会优化为偏向锁,避免 CAS 操作
1.轻量级锁:多个线程竞争锁时,JVM 会升级为轻量级锁,使用 CAS 操作
1.重量级锁:当竞争激烈时,JVM 会升级为重量级锁,线程进入阻塞状态
5.对象头与锁:Java 对象在内存中的布局分为三部分:
1.对象头(Header):包含锁信息、GC 信息等
2.实例数据(Instance Data):对象的字段数据
3.对齐填充(Padding):为了内存对齐而填充的字节
6.synchronized 的锁信息存储在对象头中,对象头的主要组成部分包括:
1.Mark Word:存储对象的哈希码、锁状态、GC 分代年龄等信息
1.Klass Pointer:指向对象所属类的元数据
7.Mark Word中锁的状态通过不同的标志位来表示
1.无锁状态:锁标志位为 01
2.偏向锁:锁标志位为 01,并记录偏向线程 ID
3.轻量级锁:锁标志位为 00
4.重量级锁:锁标志位为 10
8.锁的升级过程:JVM 对 synchronized 的锁进行了优化,锁的状态会根据竞争情况逐步升级
1.偏向锁
1.适用场景:只有一个线程访问锁
2.原理:在对象头中记录偏向线程 ID,后续该线程可以直接获取锁,无需 CAS 操作
3.优点:减少锁竞争的开销
2.轻量级锁
1.适用场景:多个线程交替访问锁,但没有竞争
2.原理:使用 CAS 操作将对象头的 Mark Word 替换为指向线程栈中锁记录的指针
3.优点:避免线程阻塞,减少上下文切换
3.重量级锁
1.适用场景:多个线程竞争锁
2.原理:将对象头的 Mark Word 替换为指向 Monitor 的指针,线程进入阻塞状态
3.优点:解决高并发竞争问题
9.synchronized 的底层实现
1.字节码层面:当方法或代码块被 synchronized 修饰时,JVM 会在字节码中插入 monitorenter 和 monitorexit 指令
1.monitorenter:获取对象的 Monitor
2.monitorexit:释放对象的 Monitor
2.JVM 层面
1.JVM 会根据锁的状态和竞争情况,选择合适的锁机制(偏向锁、轻量级锁、重量级锁)。
2.如果锁竞争激烈,JVM 会将锁升级为重量级锁,线程进入阻塞状态,等待锁释放
10.synchronized 的优化:为了提升 synchronized 的性能,JVM 引入了以下优化技术
1.锁消除(Lock Elimination):JVM 在编译时通过逃逸分析,判断锁对象是否只被一个线程访问。如果是,则消除锁
2.锁粗化(Lock Coarsening):如果多个连续的锁操作作用于同一个对象,JVM 会将多个锁合并为一个锁,减少锁的获取和释放次数
3.自适应自旋(Adaptive Spinning):在轻量级锁竞争时,线程会自旋等待锁释放。JVM 会根据历史数据动态调整自旋次数