JAVA是如何运行的五-synchronized

书接上回

当我们对一个代码块或者对象加synchronize关键字时,在早期的jdk版本中时直接申请系统锁,然后将内存锁住,这样的结果就是大大降低执行效率。在JDK1.6版本后对synchronized的实现进行了各种优化,自旋锁、偏向锁和轻量级锁

锁的4中状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态(级别从低到高)

偏向锁

偏向第一个拿到锁的线程。

即第一个拿到锁的线程,锁会在对象头 Mark Word 中通过 CAS 记录该线程 ID,该线程以后每次拿锁时都不需要进行 CAS(指轻量级锁)。

如果该线程正在执行同步代码块时有其他线程在竞争(指其他线程尝试 CAS 让 Mark Word 设置自己的线程 ID),会被升级为轻量级锁。

如果其他线程发现 Mark Word 里记的不是自己,且发现原持有偏向锁的线程已经执行完同步代码块,会尝试 CAS 把 Mark Word 中的改为自己的线程 ID。

轻量级锁

轻量级锁就是通过 CAS 进行加锁的。

JVM 会给线程的栈帧中创建一个叫锁记录 Lock Record 的空间,把对象头 Mark Word 复制到该空间里(Displaced Mark Word),并通过 CAS 尝试把原对象头 Mark Word 中锁记录指针指向该锁记录。如果成功,表示线程拿到了锁。如果失败,则进行自旋(自旋锁),自旋超过一定次数时升级为重量级锁,这时该线程会被内核挂起。

自旋锁

轻量级锁膨胀为重量级锁前,线程在执行 monitorenter 指令进入等待队列时,会通过自旋去尝试获得锁。

如果自旋超过一定次数时还未拿到锁,就会进入阻塞状态,等待内核来调度。此时会发生内核态与用户态之间的上下文切换,所以会影响性能(引入自旋锁就是为了减少这个开销)。

因为后面的线程也先进行自旋尝试获取锁,所以这对于已被阻塞的那些线程来说,会不公平

重量级锁

重量级锁就是通过内核来操作线程。因为频繁出现内核态与用户态的切换,会严重影响性能。

锁升级验证过程

在没有加锁的情况下,锁对象mark word 分别记录着对象的hashCode,堆中的分代年龄,以及锁标志位。


public class SynchronizedDemo2 {

    public static Unsafe unsafe;
    public static SynchronizedDemo2 synchronizedDemo = new SynchronizedDemo2();

    public  static void main(String[] args) throws Exception {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        unsafe = (Unsafe) theUnsafe.get(null);

        System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
        System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());
        System.out.println("对象HASH:" + getLongBinaryString(synchronizedDemo.hashCode()));
        System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
        System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());
    }

    private static String getLongBinaryString(long num) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 64; i++) {
            if ((num & 1) == 1) {
                sb.append(1);
            } else {
                sb.append(0);
            }
            num = num >> 1;
        }
        return sb.reverse().toString();
    }


}

执行结果:

一:新new出来的对象锁状态001;默认无锁。

二:当调用对象HashCode后 MarkWord将记录当前的值

MarkWord:0000000000000000000000000000000000000000000000000000000000000001

 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           05 c1 00 20 (00000101 11000001 00000000 00100000) (536920325)
     12     4        (loss due to the next object alignment)

对象HASH:00000000000000000000000000000000 01011001 00110110 00110100 10101101
MarkWord:0000000000000000000000000101100100110110001101001010110100000001

 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 ad 34 36 (00000001 10101101 00110100 00110110) (909421825)
      4     4        (object header)                           59 00 00 00 (01011001 00000000 00000000 00000000) (89)
      8     4        (object header)                           05 c1 00 20 (00000101 11000001 00000000 00100000) (536920325)
     12     4        (loss due to the next object alignment)

 

 要验证偏向锁要开启偏向锁立即生效 -XX:BiasedLockingStartupDelay=0

 

public  static void main(String[] args) throws Exception {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        unsafe = (Unsafe) theUnsafe.get(null);

        System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
//        System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());
        //调用hash会使偏向锁转换为无锁,所以这里关闭 hash代码
//        System.out.println("对象HASH:" + getLongBinaryString(synchronizedDemo.hashCode()));
//        System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
//        System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());

        synchronized (synchronizedDemo){
            System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
        }
        synchronized (synchronizedDemo){
            System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
        }


    }

 

运行结果:

当同一个线程对对象加锁,MarkWord会记录当前线程的线程指针,如果是同一个线程进行调用,则进入偏向模式。

MarkWord:0000000000000000000000000000000000000000000000000000000000000101
MarkWord:0000000000000000000000000000000000000010100001110010100000000101
MarkWord:0000000000000000000000000000000000000010100001110010100000000101


 

 

    public  static void main(String[] args) throws Exception {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        unsafe = (Unsafe) theUnsafe.get(null);

        System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
//        System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());
        //调用hash会使偏向锁转换为无锁,所以这里关闭 hash代码
//        System.out.println("对象HASH:" + getLongBinaryString(synchronizedDemo.hashCode()));
//        System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
//        System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());

        //同一个线程对其加锁
        synchronized (synchronizedDemo){
            System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
        }
        synchronized (synchronizedDemo){
            System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
        }

        //当另外一个线程对其加锁的时候进入轻量级锁状态
        Thread thread1 = new Thread(() -> {
            synchronized (synchronizedDemo){
                System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
                System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());
            }
        });
        thread1.start();
        thread1.join();
        synchronized (synchronizedDemo){
            System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
            System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());
        }


    }

运行结果:

当有其他线程开始争夺锁资源时,升级为轻量级锁或自旋锁,并且markword记录当前线程的LockRecord指针。

MarkWord:0000000000000000000000000000000000000000000000000000000000000101
MarkWord:0000000000000000000000000000000000000011001010000010100000000101
MarkWord:0000000000000000000000000000000000000011001010000010100000000101
MarkWord:0000000000000000000000000000000000011010111010001111001100001000
com.company.SynchronizedDemo2 object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           08 f3 e8 1a (00001000 11110011 11101000 00011010) (451474184)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           05 c1 00 20 (00000101 11000001 00000000 00100000) (536920325)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

MarkWord:0000000000000000000000000000000000000011000101001111011101001000
com.company.SynchronizedDemo2 object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           48 f7 14 03 (01001000 11110111 00010100 00000011) (51705672)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           05 c1 00 20 (00000101 11000001 00000000 00100000) (536920325)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

 

 

    public  static void main(String[] args) throws Exception {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        unsafe = (Unsafe) theUnsafe.get(null);

        System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
//        System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());
        //调用hash会使偏向锁转换为无锁,所以这里关闭 hash代码
//        System.out.println("对象HASH:" + getLongBinaryString(synchronizedDemo.hashCode()));
//        System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
//        System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());

        //同一个线程对其加锁
        synchronized (synchronizedDemo){
            System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
        }
        synchronized (synchronizedDemo){
            System.out.println("MarkWord:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
        }

        //当另外一个线程对其加锁的时候进入轻量级锁状态
        Thread thread1 = new Thread(() -> {
            synchronized (synchronizedDemo){
                System.out.println("MarkWord1:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
                System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("MarkWord2:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
                System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());
            }
        });
        thread1.start();
        Thread.sleep(100);
        synchronized (synchronizedDemo){
            System.out.println("MarkWord3:" + getLongBinaryString(unsafe.getLong(synchronizedDemo, 0L)));
            System.out.println(ClassLayout.parseInstance(synchronizedDemo).toPrintable());
        }


    }

执行结果:

注意代码中Thread1 是先启动的 并且等待了1000毫秒,主线程等待100毫秒是开始抢锁。

所以当Thread1持有锁的状态轻量锁,但是100毫秒后主线程开始抢锁,于是此时在争抢到达一定节点,锁升级为重量级锁。主线程必须在thread1执行完后才能拥有锁。

 

MarkWord1:0000000000000000000000000000000000011011000111111111001110100000
MarkWord2:0000000000000000000000000000000000000011011001011010010000101010
MarkWord3:0000000000000000000000000000000000000011011001011010010000101010

网上有一个很完善的图,这里贴出来。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值