Подтвердить что ты не робот

Уведомляет/уведомляет об освобождении блокировки

Я немного смущен о ожидании и уведомлении /notifyAll.

Я знаю, что для каждого java-объекта есть блокировка. Я знаю, что ожидание отпустит блокировку для другого потока. Как насчет уведомления/уведомления? Уведомляет /notifyAll освобождает блокировку, удерживаемую для другого потока?

4b9b3361

Ответ 1

Нет - notify/notifyAll не освобождать блокировки, такие как wait. Пробужденный поток не может работать до тех пор, пока код, который вызвал notify, не заблокирует его.

Вот что говорит Джавадок:

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

Ответ 2

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

  • notify() просыпает поток, который вызывает wait() на одном и том же объекте.

  • notifyAll() просыпает все потоки, которые вызывают wait() на одном и том же объекте. сначала будет выполняться поток с наивысшим приоритетом.

Ответ 3

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

Пример:

Consumer класс содержит блок:

synchronized(sharedObject){
if(sharedObject.isReadyToConsume() == false){
     sharedObject.wait();
}else {
    sharedObject.doTheThing();
    System.out.println("consumer consuming...");
 }

}

Сценарий: Класс пользователя получает блокировку объекта sharedObject, входит исключительно (внутри блока синхронизации) и видит, что sharedObject ничего не готово (ничего не потреблять:)), и он вызывает метод wait() на sharedObject. Таким образом, он освобождает блокировку (останавливает выполнение там!) И ждет, когда вас уведомят продолжить, когда другой Thread (продюсер) может вызвать sharedObject.notify(); или sharedObject.notifyAll();. Когда он получает уведомление, он продолжается с строки wait()

Он sharedObject, который отслеживает потоки, которые просят его получать уведомление. Когда какой-то поток вызывает метод sharedObject.notifyAll(), sharedObject будет уведомлять ожидающие потоки для пробуждения... Теперь сложная часть состоит в том, что поток естественно освобождает блокировку объекта, когда он достигает конца своего синхронизированного (sharedObject) {} блока. Вопрос: , что произойдет, если я вызову notifyAll() в этом блоке??? notifyAll() пробуждает ожидающие потоки, но блокировка по-прежнему принадлежит потоку, который только что вызвал notifyAll()

Посмотрите на фрагмент Продюсера:

synchronized(sharedObject){
//We are exlusively working with sharedObject and noone can enter it
[... changing the object ...]
sharedObject.notifyAll();     //notifying the waiting threads to wake up

Thread.sleep(1000);           //Telling the current thread to go to sleep. It holding the LOCK
System.out.println("awake...");

}

Если notifyAll() освободит блокировку, тогда "бодрствование..." будет распечатано после того, как классы пользователей уже начнут работать с sharedObject. Это не так... Выход показывает, что потребитель потребляет sharedObject после того, как Producer выходит из блока синхронизации...

  • wait() - освобождает блокировку и продолжается на следующей строке, когда она получает уведомление
  • notify(), notifyAll() - не освобождать блокировку. Они просто заставляют ожидающие потоки снова запускаться (не простаивают). Они будут иметь право на въезд, когда текущая нить достигает конца своего блока синхронизации и потока scheder сообщает им, что блокировка была выпущена. Борьба за блокировка начинается снова

Ответ 4

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

Поэтому для синхронизации таких случаев между Readers and Writers на общем ресурсе были использованы три конечных метода класса Object.

  • ожидание()
  • Notify()
  • notifyAll()

Подождите. Читатели хотят прочитать обновленное значение ресурса, они регистрируются в объекте ресурса, т.е. когда обновление происходит на одном и том же объекте, и когда Writer уведомит об этом, читатели попытаются получить блокировку на ресурсе и прочитать обновленный ресурс.  - Подождите, только когда вызывается, когда Reader имеет объект блокировки, здесь в нашем случае это ресурс.  - Когда вызывается метод ожидания, Reader выдает объект блокировки.  - Теперь только для того же зарегистрированного объекта (ресурса) Reader получит уведомления.  - Если Reader вызывает wait на Object, который отличается от Object Writer, используемого для отправки уведомления, Reader никогда не получит сигнал уведомления.  - После того, как Reader (s) будут уведомлены, теперь Reader (s) попытается загрузить контент для Lock (один из них получает блокировку), прочитав обновленное значение ресурса. Аналогично, другие Читатели также получают возможность получить блокировку и прочитать обновленное значение.  - После того, как Reader прочитает обновленное значение, выполните там Business Logic и выйдите из Synchronized Block, Reader выпустит блокировку, чтобы другие читатели могли ее приобрести.

Уведомлять: Writer входит в синхронизируемый блок, после того, как блокировка выполняет его бизнес-логику, обновите объект ресурса, после обновления ресурса Object он уведомит ожидающие потоки (читатели), которые ждут того же Замок.  - Уведомлять сигнал только о одном ожидающем потоке, который определяется базовым диспетчером потоков Java  - Как только Writer сигнализирует(), это не означает, что Reader сразу же бросился читать значения обновлений. Во-первых, автор должен освободить Lock, который он будет делать, когда он выйдет из Синхронизированного блока. После того, как Lock освобождается, и ожидающие потоки уведомляются, тогда [В случае уведомления()] уведомление Thread получит Lock [Released by Writer] а затем введите Синхронизированный блок и завершится с того места, где он ушел [т.е. заявления после ожидания()]. ​​

Уведомлять-все. В уведомлении All, все потоки, зарегистрированные с блокировкой ресурсов, получат уведомления.  - После срабатывания notifyAll() все потоки, ожидающие одной и той же блокировки, получат сигнал и готовы конкурировать, чтобы получить блокировку.  - Как только Writer завершит свое задание и освободит блокировку, любой один Reader получит блокировку [этот поток, опять же решенный с помощью реализации Java Thread Manager Implementation].  - Как только Reader получит Lock, он войдет в Synchronized Block, где он уйдет [i.e после метода wait()] выполняет задачи, а при завершении синхронизации синхронизируется блокировка.  - Теперь остальные оставшиеся потоки попытаются получить Lock, любой из них получит его, войдет в синхронизированный блок, выполнит свою задачу и затем отпустит Lock.  - Этот процесс будет продолжаться до тех пор, пока все зарегистрированные читатели не заполнят Job.


Теперь мы увидим код для него. Также мы также обсудим Кодекс.

Базовый обзор кода: он состоит из трех классов

  • Класс ресурсов: на котором будет получен Lock и wait() и notify(), будет вызван notifyAll().
  • ReaderTask: реализует интерфейс Runnable, подразумевает задания читателей, хочет прочитать обновленное значение ресурса.
  • WriterTask: реализует интерфейс Runnable, подразумевает задания записи, обновляет объект ресурса и уведомляет зарегистрированные ожидающие потоки.
  • Demo Class: создаст Let say 3 Readers и 1 Writer Thread, привяжите к ним соответствующие задачи и запустите потоки.

Resource.java

   public class Resource {
      private String mesg;

          public void setMesg(String mesg){
         this.mesg =mesg;
      }
      public String getMesg(){
         return this.mesg;
      }
    }

WaitThreadTask.java

public class WaitThreadTask implements Runnable {

    private Resource resource;

    public WaitThreadTask(Resource resource){
        this.resource = resource;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        synchronized(resource){
            System.out.println("Before Reading Updated Value By : " +Thread.currentThread().getName() );
            //We need to Take care to get the updated value, so waiting for writer thread to update value.
            try {
                //Release resource Lock & wait till any notification from Writer.
                resource.wait();
                System.out.println("Waiting is Over For : "+ Thread.currentThread().getName());
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //Read Updated Value
            System.out.println("Updated Value of Resource Mesg :" + resource.getMesg() + " Read By :" +Thread.currentThread().getName());
        }
    }
}

WriterThreadTask.java

public class WriterThreadTask implements Runnable{

    private Resource resource;

    public WriterThreadTask(Resource resource){
        this.resource = resource;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        synchronized(resource){
            System.out.println("Before Updating Resource By : " + Thread.currentThread().getName());
            //Updating resource Object Message
            resource.setMesg("Hi How are You !!!");
            resource.notify();
            //resource.notifyAll();
            //Once Writer Comes Out from Synch Block, Readers will Content to read the values.
            System.out.println("Task Done By Writer Thread.");
        }
    }
}

ThreadDemo.java

public class ThreadDemo {

    public static void main(String args[]){

        //Create Single Resource Object, which can act as Lock on Writer and Readers.

        Resource lock = new Resource();

        //Three Readers and One Writer runnable Tasks.
        Runnable taskR1 = new WaitThreadTask(lock);
        Runnable taskR2 = new WaitThreadTask(lock);
        Runnable taskR3 = new WaitThreadTask(lock);
        Runnable taskW1 = new WriterThreadTask(lock);

        Thread t1 = new Thread(taskR1, "Reader1");
        Thread t2 = new Thread(taskR2, "Reader2");
        Thread t3 = new Thread(taskR3, "Reader3");
        Thread t4 = new Thread(taskW1, "Writer1");

        t1.start();
        t2.start();
        t3.start();

        /*try{
            Thread.sleep(5000);
        } catch(InterruptedException e){
            e.printStackTrace();
        }*/

        t4.start();
    }

}

Кодовые наблюдения:

  • Оба notify()/notifyAll() и wait(): работают только с объектами Lock, которые они уже приобрели. Например: Synchornized (ObjectA) {......//...//ObjectB.wait() или ObjectB.notify() или ObjectB.notifyAll()...}, то он выкинет IllegalMonitorStateException. Поэтому следует позаботиться о том, чтобы блокировка должна была быть получена до вызова любого из трех методов с одинаковой блокировкой. Даже если вы просто пишете notify() или wait() или notifyAll(), то все равно он выкинет IllegalMonitorStateException, потому что [Предполагается, что блокировка должна быть приобретены на этом объекте, опять же это не так).
  • Reader сможет принимать сигналы, по которым отправляется одно и то же уведомление. Если на объекте происходит ожидание, которое отличается от объекта, по которому отправляется уведомление, то читатели никогда не получат уведомление, и поэтому они будут ждать всегда.
  • Читатели, зарегистрированные до того, как Writer может отправить уведомление, получат только те читатели. Поскольку, если Writer сначала отправляет уведомление, прежде чем читатель зарегистрируется в Object, они не получат сигналы, поскольку сигналы уже пропущены: Пропущенные сигналы
  • Reader и Writer должны получить Lock на одном объекте и должны вызывать сигналы ожидания/уведомления на том же объекте. Если приведенный выше код изменен как вместо использования ресурса для блокировок и ожидания и уведомления, если мы его используем. Что случится? Ну.. Все читатели будут ждать вечно, потому что читатели, зарегистрированные в разных объектах WaitThreadTask и писатель, уведомляют о WriterThreadTask. Поэтому ни один из Reader не будет получать сигналы уведомления, поскольку они регистрируются для приема сигналов в соответствующем объекте WaitThreadTask, а не в объекте WriterThreadTask.

Ответ 5

Чтобы прояснить мое понимание и предоставить пример для всех, чтобы показать, когда блокировка выпущена, я добавил операторы печати в следующий код после вызова уведомления()/NotifyAll():

class ThreadDemo {
    public static void main(String[] args) {
        Shared s = new Shared();
        new Producer(s).start();
        new Consumer(s).start();
    }
}

class Shared {
    private char c = '\u0000';
    private boolean writeable = true;

    synchronized void setSharedChar(char c) {
        while (!writeable)
            try {
                wait();
            } catch (InterruptedException e) {
            }

        this.c = c;
        writeable = false;
        notifyAll();
        System.out.println("setSharedChar notify() called - still in synchronized block.");
    }

    synchronized char getSharedChar() {
        while (writeable)
            try {
                wait();
            } catch (InterruptedException e) {
            }

        writeable = true;
        notifyAll();
        System.out.println("getSharedChar notify() called - still in synchronized block.");

        return c;
    }
}

class Producer extends Thread {
    private Shared s;

    Producer(Shared s) {
        this.s = s;
    }

    public void run() {
        System.out.println("Starting producer thread.");
        for (char ch = 'A'; ch <= 'Z'; ch++) {
            System.out.println("Producer thread getting ready to create a char.");
            try {
                Thread.sleep((int) (Math.random() * 1000));
            } catch (InterruptedException e) {
            }

            s.setSharedChar(ch);
            System.out.println(ch + " produced by producer.");
        }
    }
}

class Consumer extends Thread {
    private Shared s;

    Consumer(Shared s) {
        this.s = s;
    }

    public void run() {
        System.out.println("Starting consumer thread.");
        char ch;

        do {
            System.out.println("Consumer thread getting ready to read a char.");
            try {
                Thread.sleep((int) (Math.random() * 1000));
            } catch (InterruptedException e) {
            }

            ch = s.getSharedChar();
            System.out.println(ch + " consumed by consumer.");
        } while (ch != 'Z');
    }
}

Когда я запустил этот пример достаточно времени, появилась точка, в которой я, в конце концов, видел вывод программы:

...
F produced by producer.
Producer thread getting ready to create a char.
getSharedChar notify() called - still in synchronized block.
F consumed by consumer.
Consumer thread getting ready to read a char.
setSharedChar notify() called - still in synchronized block.
G produced by producer.
Producer thread getting ready to create a char.
getSharedChar notify() called - still in synchronized block.
setSharedChar notify() called - still in synchronized block.
G consumed by consumer.

Поскольку выход getSharedChar может появляться до setSharedChar, кажется, что блокировка освобождается немедленно или не требуется повторно вводить синхронизированную функцию getSharedChar() вызовом notifyAll(). Блокировка все еще может быть на месте, но если вы можете повторно войти в нее без функции, какая разница? Я смог увидеть аналогичный вывод, заменяющий notify() для notifyAll(). Это было сделано на Java 1.7.0_15 в 64-битной системе Windows 7.

Ответ 6

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

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

  • Когда приходит другой поток и звонит колокол один раз, на Java, вызывая notify() на том же объекте.
  • Когда приходит еще один поток и звонит несколько раз, один из потоков получает шанс выйти из контейнерного дома Objects. В Java мы можем сделайте это, вызвав notifyAll() на том же объекте.
  • Если у нас есть ссылка на Thread в контейнере. Вызов прерывания() объекта Thread выталкивает его из состояния ожидания и приносит ему блок исключений объекта.
  • Перегружены ожидания (длинные миллисекунды) и ждут (long millSec, int nanos). Поскольку время по теме имеет право прийти из состояния ожидания и конкурса для монитора объектов снова. В случае Поток не может получить монитор после таймаута, а затем он должен ждать только уведомлений().

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

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

Объяснение взято из http://coder2design.com/thread-communication/

Ответ 7

Вызов метода notify() для объекта освобождает блокировку этого объекта. Но это не похоже на вызов метода wait().

Итак, вот как это:

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

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