【十三】一文带你了解下 synchronized 的实现原理

synchronized具有以下特性(只考虑JDK1.8)

  • 开始是乐观锁,如果锁冲突频繁,就转换为悲观锁
  • 开始是轻量级锁,如果锁被持有时间的较长,就转换成重量级锁
  • 实现轻量级锁的时候大概率用到的自旋锁策略
  • 是一种不公平锁
  • 是一种可重入锁
  • 不是读写锁

1. synchronized的加锁过程

JVM 把 synchronized 锁分为 无锁、偏向锁、(自旋锁)轻量级锁、重量级锁。会根据情况,进行升级~
【十三】一文带你了解下 synchronized 的实现原理_第1张图片

偏向锁

偏向锁,就是线程A针对锁,先进行一个标记,如果在整个代码执行过程中,没有其他线程和线程A竞争,此时就不用真的加锁~

自旋锁

当发生其他线程和线程A竞争的时候,就升级成了自旋锁,自旋锁是很快,但是消耗大量CPU资源
如果有10个线程竞争一把锁,1个竞争上,其他9个都得等待~此时就会消耗大量的CPU资源

重量级锁

面对上述情况,就升级成了重量级锁~在内核里进行阻塞等待(意味着放弃CPU资源,由内核进行后续调度)


对于主流的JVM的实现,只能升级,不能降级!

2. 锁的消除和锁的粗化

2.1 锁消除

非必要不加锁!上述升级是在程序运行时,JVM做出的优化
而锁消除,是编译阶段做的优化:检测当前代码是否是多线程执行/是否有必要加锁。如果没有必要,又把锁写了,就会在编译过程中自动把锁去掉

StringBuffer sb = new StringBuffer();
sb.append("a");
sb.append("b");
sb.append("c");

上述代码中,此时每个 append 的调用都会涉及加锁和解锁. 但如果只是在单线程中执行这个代码, 那么这些加锁解锁操作是没有必要的, 白白浪费了一些资源开销.

2.2 锁粗化

锁的粒度:synchronized 代码块,包含的代码多少(代码越多,粒度越粗,代码越少,粒度越细)
一般写代码的时候,多数情况下,是希望锁的粒度更细一点(串行执行的代码少,并发执行的代码就多)


但是有一个场景,要频繁加锁、解锁,此时编译器就可能把这个操作优化成一个更粗粒度的锁
【十三】一文带你了解下 synchronized 的实现原理_第2张图片

  • 实际开发过程中, 使用细粒度锁, 是期望释放锁的时候其他线程能使用锁.
  • 但是实际上可能并没有其他线程来抢占这个锁. 这种情况 JVM 就会自动把锁粗化, 避免频繁申请释放锁.

你可能感兴趣的:(多线程,jvm,java,开发语言)