麻雀虽小,五脏俱全。
多线程情况下。
基本锁升级
默认的锁升级情况如下:
无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁
为什么要有锁升级?这是应对不同的并发场景的策略。因为 Java 设计团队通过研究后认为:大多数锁修饰的对象,生命周期中大多只有一个线程在竞争。在默认修饰了 synchronized 的情况下,如果资源竞争并不激烈,在一个线程访问该资源时,会从无锁升级到偏向锁。在 sychronized 的设计中,会记录对应的 owner ID 为对应的线程 ID,此时的 mark down 为 01 。
怎么实现锁升级呢?
当有其他的线程初步发起竞争时,该锁会实现升级,成为轻量级锁,在处理策略上,顾名思义,保证轻量级而不涉及到系统的内核态切换阻塞,以 CAS + 自旋的策略维持着。CAS 是保证原子性的策略,而自旋则是应对频繁的挂起和唤醒,我们假定:抢锁若干周期后,它能够获取到该资源;如果一直无法获取,保持自旋的话,就会让 CPU 资源空转消耗。设想大量的并发访问某个资源时,如果全部采用 CAS 自旋,会导致大量资源的浪费。另外,在动态配置上,有自旋周期的自适应策略。
如果线程自旋若干周期无法访问到,那么我们就可以认为该资源无法被获取,从而升级成重量级锁。重量级锁是基于操作系统原语 mutux 而构建的。也就是说,它是互斥的。在这种情况下,线程 B 在状态上会变成 blocked,进入阻塞队列。
如果线程 A 一直不释放锁呢?线程 B 不就永远访问不到了吗?那么这种情况就是设计的问题了。我们要检查内部的情况,是否发生了阻塞。
Sychronized 同步
AQS
抽象队列同步策略。