Разница между синхронизированным блоком с ожиданием / уведомлением и без них? - PullRequest
17 голосов
/ 21 октября 2011

Если я просто использую метод синхронизации, а не метод ожидания / уведомления, будет ли он по-прежнему поддерживать потокобезопасность?

Какая разница?

Спасибо заранее.

Ответы [ 6 ]

13 голосов
/ 21 октября 2011

Использование synchronized делает метод / блок доступным только для потока одновременно.Так что да, это потокобезопасный.

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

7 голосов
/ 27 ноября 2012

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

Блок

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

Следовательно, wait () используется на мониторе (объект внутри синхронизированного (..)) как механизм, позволяющий всем потокам охлаждаться и прекращать использование циклов ЦП до дальнейшего уведомления или notifyAll ().

так что-то вроде:

synchronized(monitor) {
    if( list.isEmpty() )
        monitor.wait();
}

... где-то еще ...

synchronized(monitor){
    list.add(stuff);
    monitor.notifyAll();
}
4 голосов
/ 29 августа 2016

Метод создания как синхронизирован имеет два эффекта:

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

Во-вторых, при выходе из синхронизированного метода он автоматически устанавливает отношение «до и после» с любым последующим вызовом синхронизированного метода для того же объекта. Это гарантирует, что изменения состояния объекта видны всем потокам.

Синхронизация поможет вам защитить критический код.

Если вы хотите установить связь между несколькими потоками, вы должны использовать wait () и notify () / notifyAll ()

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

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

notifyAll(): Пробуждает все потоки, которые ожидают на мониторе этого объекта. Поток ожидает на мониторе объекта, вызывая один из методов ожидания.

Простой пример использования wait () и notify (): Проблема производителя и потребителя .

Поток-потребитель должен ждать, пока поток-производитель не выдаст данные. wait () и notify () полезны в приведенном выше сценарии. За определенный период времени были введены лучшие альтернативы. Обратитесь к этой высокоуровневой параллельной странице учебника.

Проще говоря:

Используйте synchronized для защиты критической части ваших данных и защиты вашего кода.

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

Связанные вопросы SE:

Что означает «синхронизированный»?

Простой сценарий с использованием wait () и notify () в java

3 голосов
/ 21 октября 2011

Эффективный Java-элемент 69: " Учитывая сложность правильного использования wait и notify, вы должны использовать высокоуровневые утилиты параллелизма вместо ."

Избегайте использования wait () и notify(): используйте synchronized или другие утилиты из java.util.concurrent , когда это возможно.

0 голосов
/ 28 августа 2016

ожидание / уведомление требуется, когда вы хотите дождаться какого-либо условия (например, пользовательский ввод) ВНУТРИ синхронизированный блок.

Типичное использование:

synchronized(obj) {
    // do something

    while(some condition is not met) {
        obj.wait();
    }
    // do something other
}

Давайте предположим, что вы не используете wait (). Затем необходимо реализовать опрос «занято-цикл», который вам нужен, что отрицательно сказывается на производительности.

synchronized(obj) {
    // do something

    while(some condition is not met) { // busy loop }

    // do something other
}

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

0 голосов
/ 14 октября 2014

Синхронизированный блок используется, если 2 потока «одного и того же объекта» пытаются получить блокировку.Поскольку объектный класс удерживает блокировку, он знает, кому дать.Принимая во внимание, что если 2 потока (скажем, t2 и t4) из 2 объектов (t1 & t2 из obj1 и t3 & t4 из obj 2) попытаются получить блокировку, obj1 не будет знать о блокировке obj2, а obj2 не будет знать о блокировке obj1.Следовательно, используются методы ожидания и уведомления.

Например:

//example of java synchronized method  
class Table{  
 synchronized void printTable(int n){//synchronized method  
   for(int i=1;i<=5;i++){  
     System.out.println(n*i);  
     try{  
      Thread.sleep(400);  
     }catch(Exception e){System.out.println(e);}  
   }  

 }  
}  

class MyThread1 extends Thread{  
Table t;  
MyThread1(Table t){  
this.t=t;  
}  
public void run(){  
t.printTable(5);  
}  

}  
class MyThread2 extends Thread{  
Table t;  
MyThread2(Table t){  
this.t=t;  
}  
public void run(){  
t.printTable(100);  
}  
}  

public class TestSynchronization2{  
public static void main(String args[]){  
Table obj = new Table();//only one object  
MyThread1 t1=new MyThread1(obj);  
MyThread2 t2=new MyThread2(obj);  
t1.start();  
t2.start();  
}  
} 

Два потока t1 и t2 принадлежат одному объекту, поэтому синхронизация здесь работает нормально.Принимая во внимание, что

class Table{  
 synchronized void printTable(int n){//synchronized method  
   for(int i=1;i<=5;i++){  
     System.out.println(n*i);  
     try{  
      Thread.sleep(400);  
     }catch(Exception e){System.out.println(e);}  
   }  

 }  
}  

class MyThread1 extends Thread{  
Table t;  
MyThread1(Table t){  
this.t=t;  
}  
public void run(){  
t.printTable(5);  
}  

}  
class MyThread2 extends Thread{  
Table t;  
MyThread2(Table t){  
this.t=t;  
}  
public void run(){  
t.printTable(100);  
}  
}  

public class TestSynchronization2{  
public static void main(String args[]){  
Table obj = new Table();
Table obj1 = new Table();
MyThread1 t1=new MyThread1(obj);  
MyThread2 t2=new MyThread2(obj1);  
t1.start();  
t2.start();  
}  
} 

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

...