说明
JDK1.6为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁”和“轻量级锁”,所以在JDK1.6里锁一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,它会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率
Java 中锁有几种状态:无锁 → 偏向锁 → 轻量级锁 → 重量级锁
注意:在 JDK 15 的版本上面,移除了偏向锁了
锁的几种状态
图片是在百度上面找的
无锁状态
程序不会有锁的竞争。第一个线程执行完之后,第二个线程再去执行。
偏向锁
偏向锁,字面理解就是说会偏向于第一个访问锁的线程
- 当一个线程访问一个同步代码块时,首先会尝试获取偏向锁。
- 如果该对象没有被其他线程锁定,并且没有发生过竞争,那么当前线程会将对象头中的标记设置为偏向锁,并将线程ID记录在对象头中。
- 当其他线程尝试获取该对象的锁时,会检查对象头中的标记,如果是偏向锁并且线程ID与当前线程ID相同,表示可以获取锁,无需进行同步操作。
- 如果其他线程尝试获取该对象的锁时,发现对象头中的标记不是偏向锁或者线程ID不匹配,表示发生了竞争,偏向锁会升级为轻量级锁。
偏向锁的优势在于减少了同步操作的开销,适用于大部分情况下只有一个线程访问同步代码块的场景。但是如果存在多个线程竞争同一个锁的情况,偏向锁会失效,需要升级为轻量级锁或重量级锁。
一个对象刚开始实例化的时候,没有任何线程来访问它的时候。它是可偏向的,意味着,它现在认为只可能有一个线程来访问它,所以当第一个线程来访问它的时候,它会偏向这个线程,此时,对象持有偏向锁。偏向第一个线程,这个线程在修改对象头成为偏向锁的时候使用CAS操作,并将对象头中的ThreadID改成自己的ID,之后再次访问这个对象时,只需要对比ID,不需要再使用CAS在进行操作。
一旦有第二个线程访问这个对象,因为偏向锁不会主动释放,所以第二个线程可以看到对象时偏向状态,这时表明在这个对象上已经存在竞争了,检查原来持有该对象锁的线程是否依然存活,如果挂了,则可以将对象变为无锁状态,然后重新偏向新的线程,如果原来的线程依然存活,则马上执行那个线程的操作栈,检查该对象的使用情况,如果仍然需要持有偏向锁,则偏向锁升级为轻量级锁,(偏向锁就是这个时候升级为轻量级锁的)。如果不存在使用了,则可以将对象回复成无锁状态,然后重新偏向。