Путаница в ожидании, уведомлении и сне

1

У меня есть простая программа, которую я нахожу очень запутанной. Фрагмент кода выглядит следующим образом:

 class Processor{

      public void produce() Throws InterruptedException{
          synchronized(this){ 

              System.out.println("Producer Running...");
              wait();
               System.out.println("Resumed");

          }
      }


     public void consume() Throws InterruptedException{
          synchronized(this){ 
              Thread.Sleep(2000);
              System.out.println("Consumer Running... Press return key to return");
              scan.nextLine();                
              notify();  
              Thread.sleep(5000);

          }

 }

Теперь мой вопрос в том, что, когда мы вызываем wait() в методе "произвести", выполнение немедленно передается методу "употреблять". (производство и потребление выполняются в отдельных потоках). Но когда уведомление(); вызывается в методе "употреблять", выполнение не сразу переносится. Он ждет завершения Thread.sleep(5000). почему это так?

Теги:
multithreading
wait
sleep
notify

4 ответа

3
Лучший ответ

Ну, причина довольно проста.

Когда поток вызывает wait() на определенном объекте, он переходит в состояние ожидания и прекращает выполнение (он удаляется из планирования). Когда вы ожидаете, что поток освобождает все мониторы, которые он принял (и ему нужно вернуть их после пробуждения)

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

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

2

Хотя вам, кажется, недостает кода, необходимого мне для объяснения полностью точно, я сделаю все возможное, чтобы дать объяснение, которое было бы применимо, даже если бы моя догадка была неправильной.

wait() и notify() - это методы, называемые объектом mutex - в этом случае this.

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

Когда notify() вызывается в мьютексе, поток, ожидающий этого мьютекса, просыпается и пытается получить блокировку. Тем не менее, он не может сделать этого до тех пор, пока блокировка не будет доступна - в этом случае, пока блокировка (this) не будет освобождена потоком, который вызывает notify() (потребительский поток). Мьютекс освобождается только после того, как потребительский поток выходит из synchronized блока, который после Thread.sleep(5000); позвоните в свой код. sleep() не выпускает никаких мьютексов, которые текущий поток получил, поэтому первый поток должен ждать, пока второй не закончит спать и не выйдет из synchronized блока.

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

0

Причина в том, что Thread.sleep(5000L) не освобождает блокировку на мониторе объекта во время ожидания, в отличие от ожидания (5000L). Это указано в Javadoc для Thread.sleep():

... Нить не теряет права собственности на какие-либо мониторы.

В то время как javadoc для Object.wait() указывает:

... Этот метод заставляет текущий поток (называть его T) размещать себя в наборе ожидания для этого объекта, а затем отказываться от любых заявлений о синхронизации на этом объекте...

0

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

Если вы не хотите ждать 5000 миллисекунд, используйте wait (5000) вместо Thread.sleep(5000).

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

В этом случае он отпустит блокировку и вскоре закончит Thread.sleep(5000) и покинет синхронизированный блок.

Имейте в виду, что если вы вызываете производные или потребляете с разными объектами, все будет идти совершенно по-другому. Я настоятельно рекомендую прочитать эту статью.

Надеюсь, поможет! Как хорошие ответы ниже!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню