Концепции Wait () и Notify () - многопоточность Java - PullRequest
3 голосов
/ 20 декабря 2011
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." );
  }
}

Я не могу понять, как это работает. Возьмите этот поток, например. Производитель входит в метод put и вызывает notify (). Что если wait () еще не был вызван потребителем? Кроме того, как только производитель вызывает notify (), как потребитель может войти в метод get (), если производитель еще не выпустил монитор? Пожалуйста, помогите мне здесь.

Ответы [ 3 ]

2 голосов
/ 20 декабря 2011

Я предполагаю, что класс вверху - Q, в нем отсутствует какой-то код.В любом случае, общая идея заключается в том, что логические valueSet и wait() / notify() вызовы работают в тандеме.

Если потребитель уже начал ждать, он получил блокировку для экземпляра Q с помощью синхронизированного метода get(), а затем снимает его, пока он ждет.

Если потребитель неНачав ждать, производитель может заблокировать экземпляр Q, так как метод put() синхронизирован с той же блокировкой.Как только производитель выйдет из блокировки, он вызовет notify() , а также установит логическое значение valueSet в значение true .

Следующий вызов потребителя get() прочитает логическое значение прежде чем пытаться ждать , обратите внимание, что там что-то есть, возьмите содержимое n и выполните любую необходимую работу.Если значение не было установлено, то есть ничего не произошло в отсутствие потребителя, он нажал бы wait() на блокировку для новой работы, и следующий notify() разбудит его.

Обновление

В вашем сценарии в комментариях вы, по сути, спрашиваете о точно такой же ситуации, но в обратном порядке.Та же логика применима.

В настоящее время потребитель ожидает, а производитель звонит notify().Производитель в настоящее время имеет блокировку и будет продолжать удерживать блокировку в течение всего метода.Все, что notify() делает, это позволяет другому потоку, ожидающему в данный момент блокировки, знать, что, когда блокировка снята, она может попытаться снять блокировку и возобновить выполнение.В этом случае есть только один другой поток, но если бы было несколько потоков, он выбрал бы только один (чтобы разбудить всех, должен быть вызван notifyAll()).

  1. Производитель выходит из метода, сняв блокировку.
  2. Потребитель просыпается и забирает блокировку.

На данный момент неясно, пришел ли уже производитель и ждет ли онзаблокировать или если он еще не вошел в метод.Тот же тандем логического флага и wait() / notify() также применим к этому.

Прежде чем потребитель снимает блокировку, выйдя из метода, он установит для логического флага значение false и вызовет notify().

Если в данный момент производитель уже ожидает блокировки, вызов notify() сообщит ему, что он может проснуться и продолжить работу после снятия блокировки.

Если производительне ожидает вызова wait(), он должен находиться за пределами метода (возможно, ожидает входа в метод и получения блокировки таким образом).Когда потребитель выходит из метода и снимает блокировку, производитель получает его и проверяет логический флаг.Для него установлено значение false, поэтому производитель не пытается вызвать wait(), а просто сбрасывает его значение, устанавливает логический флаг и вызывает notify().

1 голос
/ 20 декабря 2011
  1. Что если wait () еще не была вызвана потребителем?
    • Сообщение будет потеряно
  2. Как только производитель вызывает notify (), как потребитель может ввести метод get (), если производитель еще не отпустил монитор?
    • Будет тупик - блокировать, пока монитор не будет выпущен.
0 голосов
/ 20 декабря 2011

Я постараюсь понять, как это работает.Например, Producer ввел метод put() и увидел, что монитор свободен, и он может выполнить действие размещения.В то время, когда он выполняет свою работу, приходит Consumer и пытается выполнить действие get().Но он терпит неудачу, так как монитор уже занят Producer, поэтому он вызывает wait(), что означает, что он заблокирован в этой строке кода, ожидая, пока какой-то другой поток вызовет notify().Хитрость в том, что он никогда больше не звонит get(), пока не получит уведомление о том, что монитор снова свободен, что означает, что он больше не будет выполнять пустые циклы.Итак, когда Producer закончил свою работу, он звонит notify() и Consumer возобновляет работу с того места, где остановился.Я знаю, что поначалу это немного сложно понять и немного сложно объяснить, но когда вы это сделаете, вы увидите, что это действительно простые и мощные вещи.Удачи!

...