以下内容只是自己的理解,可能和实际情况有出入
一、锁的实现
每一个对象都有一个对象头,对象头有另个部分,第一部分官方称为“Mark word”,用于保存对象的哈希码,GC分代年龄和锁相关的信息。另一部分保存对象在方法区对应的类信息的指针(如果是数组的话还有数组长度)。Mark word 的长度在32位虚拟机中为32位,在64位虚拟机中为64位。
Mark word 中有一部分用于记录锁的信息,在32位虚拟机中,这部分的大小为3位。第一位用于记录是否为偏向锁,后两位表示锁的锁定情况(01未锁定、00轻量级锁定、10重量级锁定、01偏向锁)。
二、偏向锁
对象的锁被线程获取后,进入偏向模式。当此线程再次请求这个对象时,无需进行相关的同步操作,从而节省操作时间。
三、轻量级锁
如果偏向锁失败,虚拟机会让线程用CAS操作申请轻量级锁,如果申请成功了,此线程便获得了该对象的轻量级锁。下一次此线程再次访问这个对象时,依然使用CAS操作申请资源,发现以及获取到了该对象的轻量级锁,就可以直接进入同步块继续执行。
四、锁膨胀(重量级锁)
偏向锁和轻量级锁只是为了在竞争很少的情况下提高性能,当线程竞争激烈时,这两个锁的操作反而会变得无意义。
于是,当另个线程竞争同一个轻量级锁时,轻量级锁就会膨胀为重量级锁。
整体流程就是,一个线程(线程1)访问一个资源,便会获得偏向锁。再次访问时,如果此线程(线程1)有该资源的偏向锁,就无需进行同步操作。若两次访问期间,有别的线程(线程2)尝试访问该资源,偏向锁失效。线程1会尝试获得轻量级锁。此时线程1仍然拥有资源的锁。若此时又有其他线程来竞争资源,轻量级锁膨胀为重量级锁。
五、自旋锁
线程没有获取到锁时,不被挂起,转而执行一个空循环(即自旋)。若干次自旋后,若能够获取到资源的锁,则继续执行。否则将被挂起。(线程被挂起会导致上下文切换,性能损失较大。)
六、锁消除
有的工具类(例如Vector)内置了锁,然而有一些情况这些资源不可能会被多个线程同时访问,此时同步操作会影响性能,并且没有意义,虚拟机会进行锁消除。