質問

I'm trying to use class as Observer and Observable. This class will be runned as thread. In run() method thread will be waiting and after getting event thread will be notifyed. There is example code:

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 output is:

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

And thread stay locked. Expected output:

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!

What's going wrong? Why I come into synchronized block before monitor released? P.S. I have an idea that problem is adding observer to MyThread object, may be i will add observer to Thread object? But how I could do this?

役に立ちましたか?

解決

Well, I think the main thing you are stumbling upon is that the synchronized keyword resembles a reentrant lock (http://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html).

That means, while you are in your MyThread's run method you are notifying the Controller. This one then calls the update of your MyThread which enters the synchronized block (since it is reentrant) and completes this method. Afterwards, the Controller.update method returns and the rest of your MyThread.run method continues, thus being stuck on this.wait().

他のヒント

Setting a breakpoint and stepping through/debugging the application will help you find the cause for this behaviour. The reason is that MyThread.update is called before the thread starts waiting, and there is no other thread to wake this thread. You would require a second thread.

In the MyThread.run method you are notifying the Controller object with this line: super.notifyObservers(new Object());

This calls the update method of the Controller object, which in turn then calls the update method of the MyThread object (by notifying it) which prints the synchronised block message.

Then the notifyObservers call in your MyThread.run returns and only then do you reach your call to the wait method.

To reach your expected result, you would need a second thread to notify your MyThread object after you have called wait.

The simplest example using the main thread requires these changes:

Remove notification in 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());
    }
}

Add notification after starting MyThread instead, this is called from the main thread.

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
}

This will yield the following result:

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

As you can see, MyThread.run continues after it gets the notification

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top