文
章
目
录
章
目
录
Java面试题:谈谈Java中synchronize的用法及其原理?
得分点:
作用于三个位置、对象头、锁升级
标准回答:
synchronized
在Java中可以作用在三个不同的位置,分别对应三种不同的使用方式,这三种方式的区别在于锁对象的不同选择:
- 当
synchronized
作用在静态方法上时,锁对象是当前类的Class
对象。 - 当
synchronized
作用在普通方法上时,锁对象是当前实例(即this
)。 - 当
synchronized
作用在代码块上时,需要在关键字后的小括号中显式指定一个对象作为锁对象。
根据要锁定的范围,应准确选择锁对象以确定锁的粒度,以降低锁带来的性能开销。
synchronized
的底层实现是依赖Java对象头来存储锁信息的,Java对象头包含三部分:Mark Word(标记字)、Class Metadata Address(类元数据地址)、Array length(数组长度,仅用于数组对象)。其中,Mark Word用于存储对象的hashCode以及锁信息。synchronized
的锁信息包括锁的标志和锁的状态,这些信息存放在对象头的Mark Word部分。
锁的升级机制是Java中的一个重要优化策略,它旨在根据线程竞争情况逐步升级锁的状态以提高效率。从Java 6开始,锁被分为四种状态,从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。锁可以升级但不能降级,这是为了减小锁竞争带来的性能开销。
加分回答:
锁升级的过程可以根据实际场景来说明:
- 初始阶段,没有线程访问同步块,同步块处于无锁状态。
- 线程1访问同步块,尝试加偏向锁,由于没有竞争,偏向锁加锁成功,此时Mark Word存储线程1的ID。
- 线程2访问同步块,尝试加偏向锁,但因为存在竞争,偏向锁加锁失败,触发偏向锁的撤销过程,将锁恢复到可竞争状态。
- 线程1和线程2共同竞争,尝试加轻量级锁,假设线程1成功获取锁,但线程2不放弃,继续自旋等待。
- 如果线程1很快执行完,线程2也会加轻量级锁成功,锁不会升级为重量级锁。如果线程1执行时间较长,线程2放弃自旋后,会发起锁膨胀的过程,将锁升级为重量级锁,线程2进入阻塞状态。
总之,锁升级的机制使锁根据竞争情况逐步升级,只有在竞争激烈时才升级为重量级锁,以减小锁带来的性能开销。这是多线程编程中的重要性能优化策略。