目录
一.synchronized剖析
1.谈谈什么是synchronized?
- 简介:synchronized是java提供我们的一种同步机制
- 用法:同步代码块、同步方法
- 分类:对象锁、类锁
- 作用:解决并发三贱客(可见性、原子性、有序性 )
- 扩展:锁优化技术(锁升级、锁粗化、锁细化)
2.谈谈synchronized的底层实现原理
- Java层:synchronized关键字
- 编译后: monitorenter、monitorexit(同步代码块显示调用,同步方法先加上ACC_SYNCHRONIZED标识,再隐式调用monitor指令)
- 底层实现: 底层实现依赖于MarkWord和MonitorObject,具体实现细节,需要结合锁膨胀过程。
3. synchronized如何保证可见性
- 加锁前:将工作内存中的数据置为失效态,重新从主内存中取数
- 解锁前: 先将工作内存中的数据刷线到主内存中,然后再释放锁
4. synchronized如何保证原子性
- 乱序原因:CPU时钟调度,导致当前线程可能还未执行完,就会释放锁资源
- 解决方案:锁机制,只有获取锁的线程可以执行同步方法、同步代码块
5. synchronized如何保证有序性
- 结论:synchronized不能避免指令重排序(例如:DCL),但是可以避免指令重排带来的问题
- 阐述:synchronized保证有序性,是通过as-if-serial语义和happes-before原则(happens-before原则是对as-if-serial的实现)。synchronized保证了同步方法的单线程环境,从而避免了指令重排带来的问题。
6. synchronized锁膨胀过程
- 锁开销:在申请锁资源时,需要涉及用户态和内核态的切换,该操作非常耗时。
- 偏向锁:①场景:研究表明,大部分场景下总是同一个线程反复获取释放锁。 ②原理:使用标识替换申请锁,该标识存在MarkWord中 ③加锁: 线程使用CAS机制将当前线程信息写入对象头中,后续再获取锁时,只需校验下标识即可。 ④锁延迟:Java中的偏向锁是有个时延的,默认是4秒。这是为了避免线程竞争激烈场景下,频繁撤销偏向锁。 ⑤锁升级:当环境中有线程申请偏向锁失败时,就会申请撤销偏向锁,将锁升级为轻量级锁。 ⑥批量撤销删除:为了优化撤销偏向锁开销,【待完善】
- 轻量级锁:①场景:多线程竞争不是很激烈的场景下 ②原理: 标志替代锁 ,这个标志同样存在MarkWord中 ③加锁: 使用CAS机制将当前线程信息写入MarkWord中,如果写入失败则自旋重试 ④锁开销: 如果有大量线程自旋重试,会加剧CPU负载。这种情况下轻量级锁的效率反而不如重量级锁,重量级锁获取失败时,会进入等待池。 ⑤锁升级:当线程自旋超过一定阈值时,就会申请撤销轻量级锁。 JDK5之前默认是10次,JDK5后引入了自适应自旋锁,该阈值不再由人为控制,由虚拟机根据当前环境信息决定。
- 重量级锁:①场景:多线程竞争激烈的场景 ②原理:给对象、类的监视器对象加锁 ③加锁:MonitorObject有个int类型的state属性,该值为零表示未被占用,非零表示已占用。加锁成功后,MonitorObject会记录当前持有者的信息。加锁失败的线程会进入MonitorObject的等待池,等待被唤醒。
7. 谈谈锁优化
- 锁消除:消除不必要的锁开销,比如:单线程环境下,使用StringBuffer
- 锁粗化:循环体中的加锁逻辑,放到循环外。
8.synchronized VS volatile
- synchronized可以保证【原子性】,volatile不能
- synchronized保证【有序性】的原理不同
- synchronized可以修饰方法、同步代码块,volatile用于修饰共享变量
9 synchronized VS Lock
- 【锁释放】 自动释放 - 手动释放
- 【类型】 关键字 - 接口
- 【中断性】 不可中断 - 可中断(tryLock)、中断(lock)
- 【公平性】 非公平锁 - 全能锁
- 【范围】 同步方法、同步代码块 - 同步代码块
10 为什么说synchronized是重量级锁?
- 现代操作系统为了系统的安全,防止用户程序影响系统程序,将系统分为内核态和用户态。
- 用户程序一般运行在用户态,在申请锁资源时,需要先切换到内核态。CPU需要保存当前数据,这个操作非常耗时。