Observer,Observable和Runnable。为什么同步块丢失监视器?

时间:2013-04-02 13:58:32

标签: java observable observers synchronized-block

我正在尝试使用class作为Observer和Observable。该类将作为线程运行。在run()方法中,线程将等待,并且在获取事件线程之后将被通知。有示例代码:

public class Runner {

    public static void main(String[] args) {
        MyThread mt = new MyThread();
        Controller c = new Controller();
        mt.addObserver(c);
        c.addObserver(mt);
        Thread t = new Thread(mt);
        t.start();
    }

}


public class MyThread extends Observable implements Observer, Runnable {

    static private Integer op = 0;

    public void run() {
      synchronized (this) {
        while (true) {
          op++;
          System.out.println(op + " Thread started");
          super.setChanged();
          super.notifyObservers(new Object());
          op++;
          System.out.println(op + " Thread send event");
          try {
            op++;
            System.out.println(op + " Thread wait");
            this.wait();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }

    @Override
    public void update(Observable arg0, Object arg1) {
      op++;
      System.out.println(op + " Thread got event");
      synchronized (this) {
        op++;
        System.out.println(op + " We are in synchronized block!");
        this.notify();
      }
    }

}


public class Controller extends Observable implements Observer {

  public void update(Observable arg0, Object arg1) {
    System.out.println("Controller get and send event");
    super.setChanged();
    super.notifyObservers(new Object());
  }

}

Getted输出是:

1 Thread started
Controller get and send event
2 Thread got event
3 We are in synchronized block!
4 Thread send event
5 Thread wait

线程保持锁定状态。预期产出:

1 Thread started
Controller get and send event
2 Thread got event
3 Thread send event
4 Thread wait
5 We are in synchronized block!

出了什么问题?为什么我在监视器发布之前进入同步块? 附:我有一个想法,问题是将观察者添加到MyThread对象,可能是我会将观察者添加到Thread对象?但我怎么能做到这一点?

2 个答案:

答案 0 :(得分:1)

嗯,我认为您遇到的主要问题是synchronized关键字类似于可重入锁(http://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html)。

这意味着,当您使用MyThread的{​​{1}}方法时,您正在通知run。然后,这会调用Controller的{​​{1}} update进入MyThread块(因为它是可重入的)并完成此方法。之后,synchronized方法会返回,并且Controller.update方法的其余部分会继续,因此会停留在MyThread.run上。

答案 1 :(得分:1)

设置断点并单步执行/调试应用程序将帮助您找到导致此行为的原因。 原因是在线程开始等待之前调用MyThread.update,并且没有其他线程可以唤醒此线程。你需要第二个线程。

MyThread.run方法中,您使用此行通知Controller对象:     super.notifyObservers(new Object());

这会调用update对象的Controller方法,然后调用update对象的MyThread方法(通过通知)打印同步阻止消息。

然后,您notifyObservers中的MyThread.run来电回复,然后才能接到wait方法的来电。

要达到预期的结果,您需要第二个线程在您等待后通知您的MyThread对象。

使用主线程的最简单示例需要进行以下更改:

删除Controller.update中的通知:

public class Controller extends Observable implements Observer {
    public void update(Observable arg0, Object arg1) {
        System.out.println("Controller get and send event");
        super.setChanged();
        // super.notifyObservers(new Object());
    }
}

在启动MyThread后添加通知,而不是从主线程调用。

public static void main(String[] args) {
    MyThread mt = new MyThread();
    Controller c = new Controller();
    mt.addObserver(c);
    c.addObserver(mt);
    Thread t = new Thread(mt);
    t.start();

    //add the following:

    try {
        Thread.sleep(1000); //sleep for a while to make sure MyThread is waiting
    } catch (InterruptedException ex) {
        Logger.getLogger(Runner.class.getName()).log(Level.SEVERE, null, ex);
    }
    c.notifyObservers(); //notify MyThread
}

这将产生以下结果:

1 Thread started
Controller get and send event
2 Thread send event
3 Thread wait
4 Thread got event
5 We are in synchronized block!
6 Thread started
Controller get and send event
7 Thread send event
8 Thread wait

如您所见,MyThread.run在收到通知后继续