最近在学习java的并发,在看资料介绍java并发的可重入性时举了个例子,代码如下 :
public class Widget {  
    public synchronized void doSomething() {  
        ...  
    }  
}  
  
public class LoggingWidget extends Widget {  
    public synchronized void doSomething() {  
        System.out.println(toString() + ": calling doSomething");  
        super.doSomething();  
    }  
}  我的问题是:LoggingWidget中的super.doSomething()这个super到底是如何调用到父类的方法的,是类似“宏替换”(就是把父类的方法加到子类的这个位置,然后在编译运行),还是如何?
这里父类子类的方法都有synchronized同步,当调用子类LoggingWidget的doSomething()时锁对象肯定是当时调用的那个LoggingWidget实例,可是问题是当执行到super.doSomething()时,要调用父类的同步方法,那此时锁对象是谁?还是LoggingWidget实例?还是什么?
谢谢!

解决方案 »

  1.   

    可以肯定地说是LoggingWidget实例。synchronized方法基本等同于将整个方法用synchronized(this){}包裹(不确定执行方式是不是相同,但是就效果来看是一样的),而这里的this指的就是实例本身,根据多态原理,this就是LoggingWidget,所以锁对象也是LoggingWidget。至于调用super的方法,要等高手。。至少从编译过的字节码上看,就跟调用普通的函数没有区别,要看JVM是怎么执行了
      

  2.   

    只存在一个对象。
    super.doSomething()编译器就知道你是调用父类的方法了。
      

  3.   

    我记得,继承,就是生成一个父类的对象,并让子类拥有该对象的引用,当然,这些操作都是对程序员不可见的。
    因此,调用super.doSomething(),就是锁住了隐含的父类对象。
    我认为这并不是对同一个锁进入了2次,而是获得了2个不同的锁:子类对象的锁和父类对象的锁
      

  4.   

    一个实验可以证明他们用的是同一个锁:
    如果super锁住了父类对象,那么另一个线程仍然可以获得子类对象的锁。按照这个假设,以下程序应该输出
    something sleepy!
    something else
    woke up!但输出的是
    something sleepy!
    woke up!
    something else所以获得的锁是同一个。或者说,不关JVM内部是怎么处理的,对于程序员来说都无关紧要,一个对象只有一个内部锁。public class Test {
      public static void main(String[] args) throws InterruptedException {
        final TestChild t = new TestChild();    new Thread(new Runnable() {
          @Override
          public void run() {
            t.doSomething();
          }
        }).start();
        Thread.sleep(100);
        t.doSomethingElse();
      }  public synchronized void doSomething() {
        System.out.println("something sleepy!");
        try {
          Thread.sleep(1000);
          System.out.println("woke up!");
        }
        catch (InterruptedException e) {
          e.printStackTrace();
        }
      }  private static class TestChild extends Test {
        public void doSomething() {
          super.doSomething();
        }    public synchronized void doSomethingElse() {
          System.out.println("something else");
        }
      }
    }
      

  5.   

    something sleepy!
    woke up!
    something else这个结果说明不了任何问题,我帮你分析一下为什么产生这个结果
    你这个程序有2个线程,main线程和 new Thread线程
    你首先执行的是new Thread线程的t.doSomething();方法
    这个方法首先获得子对象的锁,再获得父对象的锁,在父对象方法中打印something sleepy
    然后睡1秒,在睡觉过程中,他持有2个锁,子对象的锁和父对象的锁
    此时调度器选中main线程,main线程调用t.doSomethingElse();
    执行此方法需要子对象的锁,但很遗憾,子对象的锁目前仍然在new Thread手里,所以main线程阻塞
    然后new Thread睡醒了,打印woke up!
    然后释放父对象的锁
    然后释放子对象的锁
    然后main线程不在阻塞,执行t.doSomethingElse();方法,打印something else
      

  6.   

    子对象的doSomething没有加synchronized关键字
      

  7.   

    public synchronized void doSomething() 
    每次调用这个函数,就会获得一把锁,这个锁就是函数所在的类的对象。public class LoggingWidget extends Widget {  
        public synchronized void doSomething() --这里的锁是子类对象{  
            System.out.println(toString() + ": calling doSomething");  
            super.doSomething();  --这里的锁是父类对象
        }  
    }  
      

  8.   


    确实,我没看仔细
    如此看来,结论是这样的
    在子类中,调用父类的synchronized方法,锁住的是子类的对象,而不是父类的对象
      

  9.   


    确实,我没看仔细
    如此看来,结论是这样的
    在子类中,调用父类的synchronized方法,锁住的是子类的对象,而不是父类的对象这应该是个比较基础的问题啊,super这个东西的含义是什么?不是指的父类,而是:一个用来引用继承而来的成员的引用。
    那么super.doSomething()的含义是,通过super引用调用从父类继承而来的doSomething()方法,那么明显锁的还是当前的子类对象
      

  10.   

    锁住谁你可以用同步块了来试试  就是 synchronized(this),然后在doSomething方法中打印出this大致应该能看出他是哪个对象的,我是这么想的 没有试过,谈谈想法就是了
      

  11.   


    这种认为是不正确的,实际上是锁应该是同一个对象,如果是不同对象的话就这里就不会存在死锁的风险,而在资料举这个例子的时候是为了解释由于java的可重入性才解决死锁的风险
      

  12.   

    参考:http://bbs.csdn.net/topics/390416405
      

  13.   


    你的这个想法非常好,虽然我也没有试过,不过从原理上解释了我的疑问,同步方法是同步块(this)的简写形式,而如果使用this的话,不管是调用LoggingWidget.doSomething(),还是在这个方法里面调用父类的doSomething(),这个this都代表当前对象,所以是同一个,且都是LoggingWidget对象也感谢大家的回答,如果没有其他问题的话,这周我就结贴了,如果发现我上面的说法有问题,欢迎指正.