Keep going

JVM是如何实现锁[译]

原文链接

Hotspot JVM 1.6具有三种类型的锁,当你试图使用java.util.concurrent.locks.Lock的实现去获取一个锁,或者进入一个synchronized块时就会使用JVM提供的锁:

biased锁(偏向锁)

在有些时候,就算时在一个并发环境中,实际上对于某些对象来说时没有真正的竞争的,这个时候jvm就不会向操作系统申请互斥对象(mutex)来实现锁。Hotspot可以使用一些内部的数据结构来更加高效的模拟锁。比如,一段被synchronized包裹的同步代码在当前并没有并发的执行,JVM就会使用CAS来将当前拥有锁的线程ID赋给一个用来表示互斥对象的对象中,并且如果CAS成功的话还会将重入次数也存进去。这个就是偏向锁,JVM最轻量级的锁。重入次数这个变量会被锁的当前拥有线程更新,就像更新一个局部变量一样,不会使用CAS。如果CAS失败,说明该锁当前正被其他线程拥有,这个时候JVM会暂停该互斥对象的拥有线程,将线程上下文刷新的主存中,并检查重入次数。如果重入次数时0,那么JVM就会将该锁升级为thin,否则就升级为fat。Hotspot使用互斥对象中的同样的域(field)来存储锁的拥有者线程ID

thin锁

这个实际上是一个简单的自旋锁。如果自旋的时间很短的话,它可以班组节约线程切换的时间(不立刻阻塞并且发生切换,而是先自旋一段时间)。当一个线程试图获取一个已经被占用的锁,那么该线程会先自旋一段时间,直到这个锁被释放。自旋的次数由内部的jvm实现决定,通常会考虑以下因素:JVM对当前应用所搜集的一些统计信息;当前线程数;CPU使用情况和数量等等。当自旋失败后,JVM决定将该thin锁升级为fat锁

fat锁

JVM中最高级别的锁,底层会向操作系统申请系统级别的互斥对象,并且使用操作系统的调度器来对线程进行挂起和恢复。这种类型的锁的开销比前面提到的锁要大得多,因为每次锁的释放和获取都会使得JVM与底层操作系统进行互操作。