美文网首页java并发编程实战
java内置锁synchronized的可重入性

java内置锁synchronized的可重入性

作者: 史小猿 | 来源:发表于2018-06-30 09:59 被阅读7次

    当线程请求一个由其它线程持有的对象锁时,该线程会阻塞,而当线程请求由自己持有的对象锁时,如果该锁是重入锁,请求就会成功,否则阻塞。

    java中获取锁的操作的粒度是“线程”,而不是“调用”,即不是每一次调用都是建立一个锁。

    重入锁的一种实现方法是为每个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为1;此时其它线程请求该锁,则必须等待;而如果同一个线程再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为0,则释放该锁。

    看下面的例子,正是由于java的内置锁是可重入的,所以下面这段代码不会发生死锁:

    public class Child extends Father{
        public static void main(String[] args) {
            new Child().doSomething();
            
        }
        
        public synchronized void doSomething(){
            System.out.println("child");
            super.doSomething();
        }
     
    }
     
    class Father{
        public synchronized void doSomething(){
            System.out.println("Father");
        }
    }
    

    输出结果:
    child
    Father

    上述代码,子类调用doSomething方法时,除了获得自身类锁的同时还会获得父类的锁,如果内置锁不可重入,那调用super.doSomething时就会发生死锁。所以重入就是,你拿了锁,再调用该锁包含的代码可以不用再次等待拿锁。

    下面代码主要证明子类调用复写了父类的方法时,除了获得自身类锁的同时还会获得父类的锁:

    /**
     * 主要为了测试,子类覆盖父类方法后,调用子类方法时,也获取父类的锁
     * 
     */
    public class TestWidget {
     
        public static void main(String[] args) throws InterruptedException {
     
            final LoggingWidget widget = new LoggingWidget();
            Thread th1 = new Thread("th1") {
                @Override
                public void run() {
                    System.out.println(super.getName() + ":start\r\n");
                    widget.doSometing();
                }
            };
     
            Thread th2 = new Thread("th2") {
                @Override
                public void run() {
                    System.out.println(super.getName() + ":start\r\n");
                    /** 为了说明子类复写父类方法后,调用时也持有父类锁*/
                    widget.doAnother();
                    /**证明了内置锁对那些没有加synchronized修饰符的方法是不起作用的*/
                    //widget.doNother();
                    /**为了说明子类复写父类方法后,调用时也持有父类锁,也持有自己本类的锁*/
                    // widget.doMyLike();
                    /**这是两个线程,这是需要等待的,并不是继承的关系,不是重入,重入是发生在一个线程中的*/
                    //widget.doSometing();
                }
            };
     
            th1.start();
            Thread.sleep(1000);
            th2.start();
        }
    }
     
    class Widget {
        public synchronized void doSometing() {
            System.out.println("widget ... do something...");
        }
     
        public synchronized void doAnother() {
            System.out.println("widget... do another thing...");
        }
     
        public void doNother() {
            System.out.println("widget... do Nothing...");
        }
    }
     
    class LoggingWidget extends Widget {
     
        @Override
        public synchronized void doSometing() {
            try {
                System.out.println("loggingwidget do something...");
                Thread.sleep(5000);
                System.out.println("end loggingwidget do something...");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            super.doSometing();
        }
     
        public synchronized void doMyLike() {
            System.out.println("loggingwidget do my like...");
        }
    }
    
    
    

    输出结果:
    th1:start

    loggingwidget do something...
    th2:start

    end loggingwidget do something...
    widget ... do something...
    widget... do another thing...

    参考资料:http://my.oschina.net/ielts0909/blog/86150

    相关文章

      网友评论

        本文标题:java内置锁synchronized的可重入性

        本文链接:https://www.haomeiwen.com/subject/riqtuftx.html