上代码:

  1. public class MarkWordTest {
  2. @Test
  3. public void test7() throws InterruptedException {
  4. out.println("无锁...........");
  5. // JVM 虚拟机启动的时候创建
  6. MyObject obj1 = new MyObject();
  7. // 无锁
  8. System.out.println(ClassLayout.parseInstance(obj1).toPrintable());
  9. Thread.sleep(5000);
  10. // JVM 启动 5 秒后创建对象
  11. MyObject obj2 = new MyObject();
  12. // 偏向锁
  13. out.println("匿名偏向锁...........");
  14. System.out.println(ClassLayout.parseInstance(obj2).toPrintable());
  15. synchronized (obj2) {
  16. // 偏向锁
  17. out.println("偏向锁有线程id...........");
  18. System.out.println(ClassLayout.parseInstance(obj2).toPrintable());
  19. }
  20. new Thread(() -> {
  21. synchronized (obj2) {
  22. // 轻量级锁
  23. out.println("轻量级锁...........");
  24. System.out.println(ClassLayout.parseInstance(obj2).toPrintable());
  25. }
  26. }).start();
  27. }
  28. }
  1. 无锁...........
  2. com.oyb.jvm.test03.lesson07.MyObject object internals:
  3. OFFSET SIZE TYPE DESCRIPTION VALUE
  4. 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
  5. 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
  6. 8 4 (object header) ad 14 01 f8 (10101101 00010100 00000001 11111000) (-134146899)
  7. 12 4 (loss due to the next object alignment)
  8. Instance size: 16 bytes
  9. Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
  10. 匿名偏向锁...........
  11. com.oyb.jvm.test03.lesson07.MyObject object internals:
  12. OFFSET SIZE TYPE DESCRIPTION VALUE
  13. 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
  14. 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
  15. 8 4 (object header) ad 14 01 f8 (10101101 00010100 00000001 11111000) (-134146899)
  16. 12 4 (loss due to the next object alignment)
  17. Instance size: 16 bytes
  18. Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
  19. 偏向锁有线程id...........
  20. com.oyb.jvm.test03.lesson07.MyObject object internals:
  21. OFFSET SIZE TYPE DESCRIPTION VALUE
  22. 0 4 (object header) 05 78 47 03 (00000101 01111000 01000111 00000011) (55015429)
  23. 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
  24. 8 4 (object header) ad 14 01 f8 (10101101 00010100 00000001 11111000) (-134146899)
  25. 12 4 (loss due to the next object alignment)
  26. Instance size: 16 bytes
  27. Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
  28. 轻量级锁...........
  29. com.oyb.jvm.test03.lesson07.MyObject object internals:
  30. OFFSET SIZE TYPE DESCRIPTION VALUE
  31. 0 4 (object header) b0 f4 6d 21 (10110000 11110100 01101101 00100001) (560854192)
  32. 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
  33. 8 4 (object header) ad 14 01 f8 (10101101 00010100 00000001 11111000) (-134146899)
  34. 12 4 (loss due to the next object alignment)
  35. Instance size: 16 bytes
  36. Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

当主线程进入的时候,锁没有升级,此时还是偏向锁,但是当其他的线程进入的时候,偏向锁便升级为了轻量级锁。

什么是轻量级锁

多个线程在不同时段获取同一把锁,即不存在锁竞争的情况,也就没有线程阻塞。
针对这种情况,JVM采用轻量级锁来避免线程的阻塞与唤醒。

在偏向锁的时候,我们说过用CAS来替换Mark Word里面的线程ID为新线程的ID,
失败,标识之前的线程仍然存在,那么暂停之前的线程,设置偏向锁标识为0,并设置锁标志位为00,
升级为轻量级锁,会按照轻量级的方式进行竞争所。
一旦失败,就说明在与其他线程竞争锁,当前线程就舱室使用自旋锁来获取锁。
自旋:不断尝试去获取锁,一般用循环来实现。
自旋是需要消耗CPU的,如果一直获取不到锁的话,那么该线程就一直处于自旋状态,白白浪费CPU资源。

解决这个问题最简单的方法就是制定自旋的次数,例如让其循环10次,如果还没获取到锁就进入阻塞状态。
但是JDK采用了更聪明的方式——适应性自旋,简单来说,即使线程如果自旋成功了,则下次自旋的次数会更多,如果自旋失败了,则自旋的次数就会减少
自旋也不是一直进行下去的,如果自旋到一定程度(和JVM、操作系统相关),依然没有获取到锁,称为自旋失败,那么这个线程会阻塞。
同时这个锁就会升级称重量级锁。