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

Концепция позади метода wait(), notify() в классе Object

Мне просто сложно понять концепцию, поместив wait() в класс Object. По этим вопросам рассмотрим, если wait() и notifyAll() находятся в классе Thread.

class Reader extends Thread {
    Calculator c;
    public Reader(Calculator calc) {
        c = calc;
    }

    public void run() {
        synchronized(c) {                              //line 9
        try {
            System.out.println("Waiting for calculation...");
            c.wait();
        } catch (InterruptedException e) {}
            System.out.println("Total is: " + c.total);
        }
    }

    public static void main(String [] args) {
        Calculator calculator = new Calculator();
        new Reader(calculator).start();
        new Reader(calculator).start();
        new Reader(calculator).start();
        calculator.start();
    }
}

class Calculator extends Thread {
    int total;
    public void run() {
        synchronized(this) {                     //Line 31
            for(int i=0;i<100;i++) {
                total += i;
            }
             notifyAll();
        }
    } 
}

Мой вопрос в том, какая разница, которую он мог бы сделать? В строке 9 мы обнаруживаем блокировку на объекте c, а затем выполняем ожидание, которое удовлетворяет условию ожидания, что нам нужно получить блокировку объекта до того, как мы будем использовать wait, и так будет случай для уведомления. Мы приобрели блокировку объекта Калькулятор на строке 31.

4b9b3361

Ответ 1

Я просто испытываю затруднения, чтобы понять концепцию, стоящую за помещением wait() в класс объекта. С этой целью ради рассмотрим, как будто wait() и notifyAll() находятся в классе потока.

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

Если wait() и notify() были на Thread, а не то каждый поток должен был бы знать состояние каждого другого потока. Как thread1 узнает, что thread2 ожидает доступа к определенному ресурсу? Если thread1 нужно вызвать thread2.notify() ему нужно каким-то образом узнать, что thread2 ожидает. Чтобы потоки могли регистрировать ресурсы или действия, в которых они нуждаются, должен быть какой-то механизм, чтобы другие могли сигнализировать о них, когда материал был готов или доступен.

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

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

// locks should be final objects so the object instance we are synchronizing on,
// never changes
private final Object lock = new Object();
...
// ensure that the thread has a mutex lock on some key code
synchronized (lock) {
    ...
    // i need to wait for other threads to finish with some resource
    // this releases the lock and waits on the associated monitor
    lock.wait();
    ...
    // i need to signal another thread that some state has changed and they can
    // awake and continue to run
    lock.notify();
}

В вашей программе может быть любое количество объектов блокировки, каждый из которых блокирует определенный ресурс или сегмент кода. У вас может быть 100 объектов блокировки и только 4 потока. Поскольку потоки запускают различные части программы, они получают эксклюзивный доступ к одному из объектов блокировки. Опять же, они не должны знать состояние выполнения других потоков.

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

Ответ 2

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

Между тем, если второй потенциальный пользователь прибывает на АЗС, он обнаруживает, что туалет заблокирован и, следовательно, недоступен для него. Он идет в службу поддержки, но ключ отсутствует, потому что он находится в руках текущего пользователя. Когда текущий пользователь заканчивается, он открывает дверь и возвращает ключ в службу поддержки. Он не беспокоится о том, чтобы ждать клиентов. Служба поддержки предоставляет ключ ожидающему клиенту. Если во время блокировки туалета появляется более одного потенциального пользователя, они должны сформировать очередь ожидания ключа к замку. Каждая нить не знает, кто в туалете.

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

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

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

Ответ 3

Другие ответы на этот вопрос пропустят ключевой момент, что в Java существует один мьютекс, связанный с каждым объектом. (Я предполагаю, что вы знаете, что такое мьютекс или "блокировка" ). Это не относится к большинству языков программирования, которые имеют понятие "блокировки". Например, в Ruby вы должны явно создать столько объектов Mutex, сколько вам нужно.

Думаю, я знаю, почему создатели Java сделали этот выбор (хотя, на мой взгляд, это была ошибка). Причина связана с включением ключевого слова synchronized. Я считаю, что создатели Java (наивно) думали, что, включив в язык методы synchronized, людям стало бы легко писать правильный многопоточный код - просто инкапсулируйте все ваше общее состояние в объекты, объявите методы, которые обращаются к этому как synchronized, и все готово! Но так не получилось...

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

wait и notify оба полагаются на мьютексы. Может быть, вы уже понимаете, почему это так... если не могу добавить больше объяснений, но пока давайте просто скажем, что оба метода должны работать на мьютексе. Каждый объект Java имеет мьютекс, поэтому имеет смысл, что wait и notify могут быть вызваны на любой объект Java. Это означает, что они должны быть объявлены как методы Object.

Другим вариантом было бы статические методы на Thread или что-то, что в качестве аргумента принимало бы любой Object. Это было бы гораздо менее запутанным для новых Java-программистов. Но они этого не сделали. Слишком поздно менять любое из этих решений; слишком плохо!

Ответ 4

Ответ на ваш первый вопрос: каждый объект в java имеет только один lock(monitor) и wait(),notify(),notifyAll() для совместного использования мониторов, поэтому они являются частью класса Object, а не класса Thread.

Ответ 5

Проще говоря, причины следующие.

  • Object имеет мониторы.
  • Несколько потоков могут получить доступ к одному Object. Только один поток может удерживать монитор объекта за раз для методов/блоков synchronized.
  • wait(), notify() and notifyAll() метод, находящийся в классе Object, позволяет всем потокам, созданным на этом Object, взаимодействовать с другими
  • Блокировка (с использованием synchronized or Lock API) и Communication (wait() and notify()) - это две разные концепции.

Если класс Thread содержит методы wait(), notify() and notifyAll(), то он создаст ниже проблемы:

  • Thread проблема связи
  • Synchronization на объекте не будет возможным. Если каждый поток будет иметь монитор, у нас не будет никакого способа добиться синхронизации.
  • Inconsistency в состоянии объекта

Подробнее об этом см. статью.

Ответ 6

Эти методы работают над замками, а блокировки связаны с объектами, а не с потоками. Следовательно, он находится в классе Object.

Методы wait(), notify() и notifyAll() - это не только просто методы, это утилита синхронизации и используется в механизме связи между потоками в Java.

Для более подробного объяснения, пожалуйста, посетите: http://parameshk.blogspot.in/2013/11/why-wait-notify-and-notifyall-methods.html

Ответ 7

Это всего лишь мои 2 цента по этому вопросу... не уверен, что это справедливо во всей полноте.

Каждый объект имеет монитор и waitset → набор потоков (это, вероятно, больше на уровне ОС). Это означает, что монитор и waitset можно рассматривать как частные члены объекта. Методы wait() и notify() в классе Thread означали бы публичный доступ к waitset или использование методов get-set для изменения waitset. Вы бы не хотели этого делать, потому что это плохое проектирование.

Теперь, учитывая, что Object знает поток /s, ожидающий своего монитора, должно быть задание объекта идти и пробуждать те потоки, ожидающие его, а не объект класса потока, идущий и пробуждающий каждый из них ( что было бы возможно, только если объекту класса потока предоставляется доступ к waitset). Тем не менее, это не задача определенного потока, чтобы идти и пробуждать каждый из ожидающих потоков. (Это именно то, что было бы, если бы все эти методы находились внутри класса Thread). Его задача - просто освободить замок и двигаться вперед со своей собственной задачей. Нить работает независимо и не нуждается в том, чтобы другие потоки ждали мониторинга объектов (это ненужная деталь для объекта класса потока). Если он начал пробуждать каждый поток сам по себе.. он отходит от своей основной функциональности и выполняет свою собственную задачу. Когда вы думаете о сцене, где может быть 1000 нитей, вы можете предположить, какую часть влияния на производительность она может создать. Следовательно, учитывая, что Object Class знает, кто его ждет, он может выполнять задание, пробуждающее ожидающие потоки, и поток, который отправил notify(), может выполнять с последующей обработкой.

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

Сообщите мне, если это звучит правильно... (У меня есть возможность иногда путать моими словами).

Ответ 8

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

Альтернативно wait и notify могли бы жить и в классе Thread. чем вместо wait(), возможно, нам придется вызвать Thread.getCurrentThread(). wait(), то же самое с уведомлением. Для операций ожидания и уведомления есть два обязательных параметра: один - это поток, который будет ждать или уведомлять другое о неявной блокировке объекта. оба они могут быть доступны как в Object, так и в классе потоков. wait() в классе Thread сделал бы то же самое, что и в классе Object, переход текущего потока в ожидаемое состояние ожидания на последнем замке.

Итак, да, я думаю, что wait и notify могли бы быть там и в классе Thread, но это больше похоже на конструктивное решение сохранить его в классе объектов.

Ответ 9

wait - wait метод сообщает текущему потоку отказываться от монитора и спать.

notify - Пробуждает один поток, ожидающий этого монитора объекта.

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

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

Ответ 10

метод wait() освободит блокировку указанного объекта и ждет, когда он сможет восстановить блокировку.

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

Причина, по которой блокировки являются частью объектов, заключается в том, что ресурсы (ОЗУ) определены Object, а не Thread.

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

Ответ 11

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

Я вызвал wait и уведомляю о классе ObjB, и это класс Thread, поэтому мы можем сказать, что wait и notify вызываются на любой объект.

public class ThreadA {
    public static void main(String[] args){
        ObjB b = new ObjB();
        Threadc c = new Threadc(b); 
        ThreadD d = new ThreadD(b);
        d.setPriority(5);
        c.setPriority(1);
        d.start();
        c.start();
    }
}

class ObjB {
    int total;
    int count(){
        for(int i=0; i<100 ; i++){
            total += i;
        }
        return total;
    }}


class Threadc extends Thread{
    ObjB b;
    Threadc(ObjB objB){
        b= objB;
    }
    int total;
    @Override
    public void run(){
        System.out.print("Thread C run method");
        synchronized(b){
            total = b.count();
            System.out.print("Thread C notified called ");
            b.notify();
        }
    }
}

class ThreadD extends Thread{
    ObjB b;
    ThreadD(ObjB objB){
        b= objB;
    }
    int total;
    @Override
    public void run(){
        System.out.print("Thread D run method");
        synchronized(b){
            System.out.println("Waiting for b to complete...");
            try {
                b.wait();
                System.out.print("Thread C B value is" + b.total);
                } 
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
    }
}