• java-锁膨胀的过程


    先来看个奇怪的demo

    public class A {
        int i=0;
       // boolean flag =false;
        public synchronized void parse(){
            i++;
            JOLExample6.countDownLatch.countDown();
        }
    }

    睡眠5秒,测试

    public class JOLExample3 {
       static A a;
        public static void main(String[] args) throws Exception {
            Thread.sleep(5000);
            a= new A();
            //a.hashCode();
            out.println("befor lock");
            out.println(ClassLayout.parseInstance(a).toPrintable());//无锁:偏向锁?
            synchronized (a){
                out.println("lock ing");
                out.println(ClassLayout.parseInstance(a).toPrintable());
            }
    
            out.println("after lock");
            out.println(ClassLayout.parseInstance(a).toPrintable());
        }
    }

    我注释的那行代码是什么锁?看下结果

     可以看出,没有线程持有锁的时候,是可偏向状态

     然后我们把睡眠的代码注释掉,再测试一下

    //Thread.sleep(5000);

    看下结果

     再看个两个线程的demo

    首先是两个线程交替执行:

    public class JOLExample10 {
       static A a;
        public static void main(String[] args) throws Exception {
            a= new A();
            Thread t1 = new Thread(){
                @Override
                public void run() {
                    synchronized (a){
                        out.println("t1 lock ing");
                        out.println(ClassLayout.parseInstance(a).toPrintable());
                    }
                }
            };
            t1.start();
           Thread.sleep(10000);//睡眠10秒,让main线程和t1线程交替执行
            synchronized (a){//a b c c+++
                out.println("main lock ing");
                out.println(ClassLayout.parseInstance(a).toPrintable());
            }
            out.println("after lock");
            out.println(ClassLayout.parseInstance(a).toPrintable());
        }
    }

    看下结果

     可以看出,交替执行时,是轻量锁

    我们把睡眠的代码注释掉

    //Thread.sleep(5000);//睡眠10秒,让main线程和t1线程交替执行

    再次测试,

    public class JOLExample10 {
       static A a;
        public static void main(String[] args) throws Exception {
            a= new A();
            Thread t1 = new Thread(){
                @Override
                public void run() {
                    synchronized (a){
                        out.println("t1 lock ing");
                        out.println(ClassLayout.parseInstance(a).toPrintable());
                    }
                }
            };
            t1.start();
           //Thread.sleep(5000);//睡眠10秒,让main线程和t1线程交替执行
            synchronized (a){//a b c c+++
                out.println("main lock ing");
                out.println(ClassLayout.parseInstance(a).toPrintable());
            }
            Thread.sleep(5000);//睡眠10秒
            out.println("after lock");
            out.println(ClassLayout.parseInstance(a).toPrintable());
        }
    }

    看下结果

    自旋

    自旋一段时间,可以理解为空转,时间很短,具体时间需要看jvm源码,如果在自旋时间内拿到了锁,就不再膨胀,如果还是拿不到锁,则膨胀为重量锁,如下

    public static void main(String[] args) throws Exception {
            a= new A();
            Thread t1 = new Thread(){
                @Override
                public void run() {
                    synchronized (a){
                        out.println("t1 lock ing");
                        out.println(ClassLayout.parseInstance(a).toPrintable());
                    }
                }
            };
            t1.start();
           Thread.sleep(1670);//睡眠10秒,让main线程和t1线程交替执行
            synchronized (a){//自旋一段时间,可以理解为时间很短,具体时间需要看jvm源码,如果在自旋时间内拿到了锁,就不再膨胀,如果还是拿不到锁,则膨胀为重量锁
                out.println("main lock ing");
                out.println(ClassLayout.parseInstance(a).toPrintable());
            }
    
            Thread thread2 = new Thread(){
                @Override
                public void run() {
                    synchronized (a){
                        out.println("t2 lock ing");
                        out.println(ClassLayout.parseInstance(a).toPrintable());
                    }
                }
            };
            thread2.start();
            /*Thread.sleep(10);
            synchronized (a){
                out.println("main lock ing");
                out.println(ClassLayout.parseInstance(a).toPrintable());
            }*/
            //Thread.sleep(5000);//睡眠10秒,让main线程和t1线程交替执行
            out.println("after lock");
            out.println(ClassLayout.parseInstance(a).toPrintable());
        }

    如果调用wait方法,则立即变成重量锁

    看下demo

    public class JOLExample11 {
        static A a;
        public static void main(String[] args) throws Exception {
            //Thread.sleep(5000);
            a = new A();
            out.println("befre lock");
            out.println(ClassLayout.parseInstance(a).toPrintable());
    
            Thread t1= new Thread(){
                public void run() {
                    synchronized (a){
                        try {
                            synchronized (a) {
                                System.out.println("before wait");
                                out.println(ClassLayout.parseInstance(a).toPrintable());
                                a.wait();//如果调用wait方法,则立即变成重量锁
                                System.out.println(" after wait");
                                out.println(ClassLayout.parseInstance(a).toPrintable());
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            t1.start();
            Thread.sleep(7000);
            synchronized (a) {
                a.notifyAll();
            }
        }
    }

    看下结果

     我们再看个synchronized膨胀的奇怪特性

    让偏向锁无延迟启动

    -XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
    public class JOLExample12 {
        static List<A> list = new ArrayList<A>();
        public static void main(String[] args) throws Exception {
            Thread t1 = new Thread() {
                public void run() {
                    for (int i=0;i<10;i++){
                        A a = new A();
                        synchronized (a){
                            System.out.println("111111");
                            list.add(a);
                        }
                    }
    
                }
    
            };
            t1.start();
            t1.join();
            out.println("befre t2");
            //偏向
            out.println(ClassLayout.parseInstance(list.get(1)).toPrintable());
            Thread t2 = new Thread() {
                int k=0;
                public void run() {
                    for(A a:list){
                       synchronized (a){
                           System.out.println("22222");
                           if (k==4){
                               out.println("t2 ing");
                               //轻量锁
                               out.println(ClassLayout.parseInstance(a).toPrintable());
    
                           }
                       }
                        k++;
                    }
    
                }
            };
            t2.start();
        }
    }

    t1线程new10个对象,t2线程取第五个,看下结果

     我们把new对象的数量改一下,改成20个,再来试一下

    public class JOLExample12 {
        static List<A> list = new ArrayList<A>();
        public static void main(String[] args) throws Exception {
            Thread t1 = new Thread() {
                public void run() {
                    for (int i=0;i<20;i++){
                        A a = new A();
                        synchronized (a){
                            System.out.println("111111");
                            list.add(a);
                        }
                    }
    
                }
    
            };
            t1.start();
            t1.join();
            out.println("befre t2");
            //偏向
            out.println(ClassLayout.parseInstance(list.get(1)).toPrintable());
            Thread t2 = new Thread() {
                int k=0;
                public void run() {
                    for(A a:list){
                       synchronized (a){
                           System.out.println("22222");
                           if (k==19){
                               out.println("t2 ing");
                               //轻量锁
                               out.println(ClassLayout.parseInstance(a).toPrintable());
    
                           }
                       }
                        k++;
                    }
    
                }
            };
            t2.start();
        }
    }

    这里我们取第20个对象,查看对象头信息

     我们可以看到,居然重偏向了,这里是jvm做的优化,20次以后就会冲偏向,小于20次时膨胀为轻量锁

    这里我们称之为批量偏向,下面我们看下这个的原理

     最后总结一下:轻量锁释放的时候将mark word重置为无锁状态,附上网络上的图

  • 相关阅读:
    ASP.NET——基础 14、Server
    ASP.NET——基础 15、HttpHandler
    ASP.NET——基础 2、ViewState
    MYSQL VS2010 中文乱码问题
    ASP.NET——基础 10、Web开发的一些基本原则1
    数据结构与算法 2、单向/双向链表
    C#操作XML日志
    web开发入门级学习经验(菜鸟级)
    笔记:键盘键值一览表(JS)
    笔记:JS事件的捕获与冒泡
  • 原文地址:https://www.cnblogs.com/lusaisai/p/12763961.html
Copyright © 2020-2023  润新知