Wait() and Notify() concepts - Java Multithreading

五迷三道 提交于 2019-12-01 05:47:40

问题


class Q {
  volatile boolean valueSet = false;
  volatile int n;

  synchronized int get () {
    if ( !valueSet ) {
      try {
        wait();
      } catch ( InterruptedException e ) {
        System.out.println( "InterruptedException caught" );
      }
    }

    System.out.println( "Got: " + n );
    valueSet = false;
    notify();
    return n;
  }

  synchronized void put ( int n ) {
    if ( valueSet ) {
      try {
        wait();
      } catch ( InterruptedException e ) {
        System.out.println( "InterruptedException caught" );
      }
    }

    this.n = n;
    valueSet = true;
    System.out.println( "Put: " + n );
    notify();
  }
}

class Producer
    implements Runnable {
  Q q;
  Producer ( Q q ) {
    this.q = q;
    new Thread( this, "Producer" ).start();
  }

  public void run () {
    int i = 0;
    while ( true ) {
      q.put( i++ );
    }
  }

}

class Consumer
    implements Runnable {
  Q q;
  Consumer ( Q q ) {
    this.q = q;
    new Thread( this, "Consumer" ).start();
  }

  public void run () {
    while ( true ) {
      q.get();
    }
  }

}

class PCFixed {
  public static void main ( String args[] ) {
    Q q = new Q();
    new Producer( q );
    new Consumer( q );
    System.out.println( "Press Control-C to stop." );
  }
}

I'm unable to understand how this works. Take this flow for instance. Producer enters put method and calls notify(). What if wait() has not been called by the consumer yet? Also, once the producer calls notify(), how can the consumer enter the method get() when the producer has not let go of the monitor yet? Please help me out here.


回答1:


I'm assuming that the class at the top is Q, it's missing some code. Anyway, the general idea is that the boolean valueSet and the wait()/notify() calls work in tandem.

If the consumer has already begun to wait, he has acquired the lock to the Q instance through the synchronized get() method and then releases it while he waits.

If the consumer has not begun to wait yet, the producer may have the lock to the Q instance because the put() method is synchronized on that same lock. Once the producer exits the lock, he will have called notify() as well as set the valueSet boolean to true.

The consumer's next call to get() will read the boolean value before attempting to wait, notice that something's there, take the contents of n and do whatever work is needed. If the value had not been set, meaning that nothing had come in in the consumer's absence, he would wait() on the lock for new work and the next notify() will wake him up.

Update

In your scenario in the comments, you're essentially asking about the exact same situation but in reverse. The same logic applies.

The consumer is currently waiting and the producer calls notify(). The producer currently has the lock and will continue to hold the lock for the duration of the method. All notify() does is let one other thread currently waiting on the lock know that, when the lock is released, it can try to take the lock and resume execution. In this case, there is only one other thread, but if there were multiple threads then it would select only one (to wake everyone, notifyAll() should be called).

  1. The producer exits the method, releasing the lock.
  2. The consumer wakes up and takes the lock.

At this point, it's ambiguous as to whether the producer has already come around and is currently waiting on the lock or if it has not entered the method yet. The same tandem of the boolean flag and wait()/notify() applies to this as well.

Before the consumer releases the lock by exiting the method, it will both set the boolean flag to false and call notify().

If the producer is currently already waiting on the lock, calling notify() will let it know that it can wake up and continue when the lock is released.

If the producer is not waiting through a wait() call, it must be outside the method (possibly waiting to enter the method and acquire the lock that way). When the consumer exits the method and releases the lock, the producer acquires it and checks the boolean flag. It's been set to false, therefore the producer does not attempt to call wait() and just drops its value off, sets the boolean flag and calls notify().




回答2:


  1. What if wait() has not been called by the consumer yet?
    • The message will be lost
  2. Once the producer calls notify(), how can the consumer enter the method get() when the producer has not let go of the monitor yet?
    • It will deadlock - block until it the monitor is realeased.



回答3:


I'll try to understand how this works. For example, the Producer has entered the put() method and saw that the monitor is free and he can execute the putting action. At the time he's doing his job here comes the Consumer and tries to execute the get() action. But he fails since the monitor is already taken by the Producer, so he calls wait() meaning he is blocked at that line of code waiting for some other thread to call notify(). The trick here is that he never again calls get() before he's been notified that the monitor is free again, means he won't execute any more empty cycles. So, when the Producer has finished his work, he calls notify() and the Consumer resumes where he left off. I know it's a bit hard to understand at first and a bit hard to explain too, but when you do you'll see that these are really simple and powerful things. Good luck!



来源:https://stackoverflow.com/questions/8579934/wait-and-notify-concepts-java-multithreading

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!