锁升级机制的极简版
升级路线:无锁->偏向锁->轻量锁->重量锁
锁升级机制适用的锁:
Synchronized
意味着除了Synchronized之外所有的锁都不遵循锁升级机制。
无锁:
没有锁竞争,不用管
偏向锁:
jvm会给第一个进入代码块的线程加上偏向锁,当这个线程第二次进入代码块的时候,会判断是否有其他线程和他进行竞争,如果没有,那么偏向锁就一直挂在这个上面,如果有线程和他进行竞争,那么持有偏向锁的线程就会被挂起。
偏向锁升级成为轻量级锁的条件:
两个及两个以上线程进行锁竞争,第一个进入代码块的线程(也就是持有偏向锁的线程)被挂起,锁升级为轻量级锁。
升级过程中造成的影响:
在偏向锁升级为轻量级锁的过程中,偏向锁的撤下会导致java程序全局停止,也就是stw(Stop The World)。
轻量级锁:
在轻量级锁锁竞争的情况下,如果某个线程锁竞争失败,那么jvm会让其自旋一段时间(这个过程会消耗CPU资源,也就是自旋锁。)如果还没有抢到锁,那么就会被挂起。
自旋锁并不是一个真正的锁
自旋锁,其实自旋是jvm的一个策略,并没有一个锁就是自旋锁,是轻量级锁更新为重量级锁过程中采取的缓冲策略,减少被挂起的线程的数量。
当锁竞争十分严重的情况下,会有大量的轻量级锁竞争线程进入自旋状态,那么这个时候对CPU的消耗是十分严重并且没有实习意义的。所以进行锁升级,升级为最后的重量级锁。
重量级锁:
锁升级的最终形态。当一个线程发现竞争的锁是重量级锁的时候,会直接把自己fork起来,只有锁的释放才能让那些被挂起的线程被唤醒。
惊群问题:
多个被挂起的线程同时被唤醒进行锁竞争。
如果过多的线程竞争同一把重量级锁,当锁被释放时,大量的线程同时被唤起,可是只有一个线程能够获得重量级锁,过多的线程被唤起又被挂起,导致了大量的资源浪费。
总结:
偏向锁:
加锁和解锁不需要额外的消耗,但是一旦进入锁竞争,就会导致stw。适用于只有一个线程的情况下。
轻量级锁:
线程一旦锁竞争失败并不会把自己挂起来,而是会进入自旋,这个极大地加快了相应的时间,但是在自旋的时候会空耗CPU资源,所以轻量级锁适用于追求响应速度的程序,并且同步执行代码块执行时间比较短的情况。
重量级锁:
线程一旦锁竞争失败,就会把自己挂起,不会像轻量级锁那样自旋。
这样可以提高程序的数据吞吐量,坏处是相应的时间变慢,适用于同步执行的代码块执行时间比较长的情况下。