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

Как можно вызвать методы wait() и notify() в объектах, которые не являются нитями?

Как методы wait() и notify() вызывать на объектах, которые не являются нитями? Это не имеет смысла, не так ли?

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

4b9b3361

Ответ 1

Блокировка - это защита общих данных.

Блокировка находится на защищаемой структуре данных. Потоки - это вещи, которые обращаются к структуре данных. Блокировки на объекте структуры данных, чтобы препятствовать тому, чтобы потоки получили доступ к структуре данных небезопасным способом.

Любой объект может быть использован в качестве внутренней блокировки (то есть используется в сочетании с synchronized). Таким образом, вы можете защитить доступ к любому объекту, добавив синхронизированный модификатор к методам, которые получают доступ к общим данным.

Методы wait и notify вызываются для объектов, которые используются в качестве блокировок. Замок является общей точкой связи:

  • Когда поток с блокировкой вызывает notifyAll, другие потоки, ожидающие этой блокировки, получают уведомление. Когда поток, имеющий блокировку, вызывает notify, один из потоков, ожидающих этой блокировки, получает уведомление.

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

См. Руководство Oracle по защищенным блокам, класс Drop - это общая структура данных, к которой обращаются потоки, использующие исполняемые модули Producer и Consumer. Блокировка объекта Drop управляет доступом потоков к данным объекта Drop.

Потоки используются в качестве блокировок в реализации JVM, разработчикам приложений рекомендуется избегать использования потоков в качестве блокировок. Например, документация для Thread.join гласит:

Эта реализация использует цикл вызовов this.wait, обусловленный this.isAlive. Когда поток завершает работу, вызывается метод this.notifyAll. Рекомендуется, чтобы приложения не использовали wait, notify или notifyAll для экземпляров Thread.

В Java 5 введены явные блокировки, реализующие java.util.concurrent.locks.Lock. Они более гибкие, чем неявные блокировки; Существуют методы, аналогичные ожиданиям и уведомлениям (ожидание и сигнал), но они находятся в состоянии, а не в блокировке. Наличие нескольких условий позволяет ориентироваться только на те потоки, которые ожидают уведомления определенного типа.

Ответ 2

Вы можете использовать wait() и notify() для синхронизации вашей логики. В качестве примера

synchronized (lock) {
    lock.wait(); // Will block until lock.notify() is called on another thread.
}

// Somewhere else...
...
synchronized (lock) {
    lock.notify(); // Will wake up lock.wait()
}

с lock является членом класса Object lock = new Object();

Ответ 3

Вы можете остановить поток на время, как вы хотите, используя статический метод Thread class sleep().

public class Main {
    //some code here

    //Thre thread will sleep for 5sec.
    Thread.sleep(5000);   
}

Если вы хотите остановить некоторые объекты, вам нужно вызвать этот метод в блоках syncronized.

public class Main {

//some code

public void waitObject(Object object) throws InterruptedException {
    synchronized(object) {
        object.wait();
    }
}

public void notifyObject(Object object) throws InterruptedException {
    synchronized(object) {
        object.notify();
    }
}

}

P.S. Я сори, если ошибаюсь, понимаю ваш вопрос (английский не мой родной)

Ответ 4

Когда вы помещаете некоторый код внутри синхронизированного блока:

 sychronized(lock){...}

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

sychronized(this){...}

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

while(balance < amountToWithdraw){
    lock.wait();
}

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

balance += amountToDeposit;
lock.signallAll;

Как вы видите, методы ждут и уведомляют только смысл внутри синхронизированных блоков или методов.

Ответ 5

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

  1. Заблокируйте дверь туалета, чтобы все остальные знали, что ее используют другие, когда пытаются открыть дверь.
  2. Идите к каждому человеку в офисе, запирайте его на свои стулья (или стол, или что-то еще), идите в уборную.

Какой вариант вы бы выбрали?

Да, это то же самое в Javaland!

Итак, в приведенной выше истории,

  • Санузел = объект, который вы хотите заблокировать (который нужен только вам)
  • Ваши коллеги по работе = другие темы, которые вы хотите не пускать

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

(Да, да! Это очень простое описание того, что происходит. Конечно, реальная концепция немного отличается от этого, но это отправная точка)

Ответ 6

В Java весь объект реализует эти два метода, очевидно, если нет монитора, эти два метода бесполезны.

Ответ 7

  • Ожидание и уведомление - это не просто обычные методы или утилита синхронизации, более того, они являются механизмом связи между двумя потоками в Java. Класс Object - это правильное место, чтобы сделать их доступными для каждого объекта, если этот механизм недоступен через любое ключевое слово java, например, синхронизированное. Помните, синхронизированы и ждать уведомления являются две разные области и не путать, что они такие же или связанные. Синхронизировано, чтобы обеспечить взаимное исключение и обеспечить безопасность потоков Java-класса, например, состояние гонки, в то время как ожидание и уведомление - это механизм связи между двумя потоками.
  • Блокировки доступны для каждого объекта, что является еще одной причиной ожидания и уведомления в классе Object, а не в классе Thread.
  • В Java, чтобы войти в критический раздел кода, потоки нуждаются в блокировке, и они ждут блокировки, они не знают, какие потоки содержат блокировку, вместо этого они просто знают, что блокировка удерживается каким-то потоком, и они должны ждать блокировки зная, какой поток находится внутри синхронизированного блока, и попросить их освободить блокировку. эта аналогия соответствует ожиданиям и уведомляет об объектном классе, а не потоке в Java.

Аналогия: поток Java - это пользователь, а туалет - это блок кода, который хочет выполнить поток. Java обеспечивает способ блокировки кода для потока, который в настоящее время выполняется с помощью синхронизированного keywokd, и создания других потоков, которые хотят использовать его, до тех пор, пока не завершится первый поток. Эти другие потоки помещаются в состояние ожидания. Java не является FAIR в качестве станции обслуживания, потому что нет очереди для ожидающих потоков. Любой из ожидающих потоков может получить монитор дальше, независимо от того, какой заказ он запросил. Единственная гарантия заключается в том, что все потоки рано или поздно получат доступ к контролируемому коду.

Источник

Если вы посмотрите на следующий код производителя и потребителя:
sharedQueue Объект выполняет межпоточную связь между потоками producer and consumer.

import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ProducerConsumerSolution {

    public static void main(String args[]) {
        Vector<Integer> sharedQueue = new Vector<Integer>();
        int size = 4;
        Thread prodThread = new Thread(new Producer(sharedQueue, size), "Producer");
        Thread consThread = new Thread(new Consumer(sharedQueue, size), "Consumer");
        prodThread.start();
        consThread.start();
    }
}

class Producer implements Runnable {

    private final Vector<Integer> sharedQueue;
    private final int SIZE;

    public Producer(Vector<Integer> sharedQueue, int size) {
        this.sharedQueue = sharedQueue;
        this.SIZE = size;
    }

    @Override
    public void run() {
        for (int i = 0; i < 7; i++) {
            System.out.println("Produced: " + i);
            try {
                produce(i);
            } catch (InterruptedException ex) {
                Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex);
            }

        }
    }

    private void produce(int i) throws InterruptedException {

        // wait if queue is full
        while (sharedQueue.size() == SIZE) {
            synchronized (sharedQueue) {
                System.out.println("Queue is full " + Thread.currentThread().getName() + " is waiting , size: "
                        + sharedQueue.size());

                sharedQueue.wait();
            }
        }

        // producing element and notify consumers
        synchronized (sharedQueue) {
            sharedQueue.add(i);
            sharedQueue.notifyAll();
        }
    }
}

class Consumer implements Runnable {

    private final Vector<Integer> sharedQueue;
    private final int SIZE;

    public Consumer(Vector<Integer> sharedQueue, int size) {
        this.sharedQueue = sharedQueue;
        this.SIZE = size;
    }

    @Override
    public void run() {
        while (true) {
            try {
                System.out.println("Consumed: " + consume());
                Thread.sleep(50);
            } catch (InterruptedException ex) {
                Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
            }

        }
    }

    private int consume() throws InterruptedException {
        //wait if queue is empty
        while (sharedQueue.isEmpty()) {
            synchronized (sharedQueue) {
                System.out.println("Queue is empty " + Thread.currentThread().getName()
                                    + " is waiting , size: " + sharedQueue.size());

                sharedQueue.wait();
            }
        }

        //Otherwise consume element and notify waiting producer
        synchronized (sharedQueue) {
            sharedQueue.notifyAll();
            return (Integer) sharedQueue.remove(0);
        }
    }
}

Источник

Ответ 8

На самом деле, функция wait, notify не должна принадлежать потоку, вещь, которой она должна принадлежать name как условная переменная, которая поступает из потока posix. И вы можете посмотреть, как cpp оборачивает эту концепцию, она оборачивает ее в выделенное имя класса std :: condition_variable.

Я думаю, что cpp делает инкапсуляцию лучше, чем java, java делает это слишком много, это напрямую помещает концепцию в класс Object, что вначале сбивает с толку людей.

Ответ 9

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