Разница между синхронизированным блоком с wait/notify и без них?

Если я просто использую synchronized, а не wait/notify метод, будет ли он по-прежнему держать потокобезопасным ?

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

Thx заранее.

6 ответов


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

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


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

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

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

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

synchronized(monitor) {

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


...somewhere else...

synchronized(monitor){

    list.add(stuff);
    monitor.notifyAll();

}

метод изготовления как синхронизироваться имеет два последствия:

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

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

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

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

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

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

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

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

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

простыми словами:

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

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

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

что означает "синхронизировано"?

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


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

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


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

например:

//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 здесь.


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

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

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() из другого потока, пробужденный поток делает не гарантированно немедленно возобновить его выполнение. Если есть другие потоки, ожидающие выполнения синхронизированного блока на том же объекте, то пробужденный поток должен конкурировать с потоками.