同步块与等待/通知和没有它们之间的区别?

时间:2011-10-20 21:06:36

标签: java multithreading wait synchronized notify

如果我只使用synchronized,而不是wait / notify方法,它是否仍然保持线程安全?

有什么区别?

提前谢谢。

6 个答案:

答案 0 :(得分:12)

使用synchronized使方法/块一次只能在线程上访问。所以,是的,它是线程安全的。

这两个概念结合在一起,而不是相互排斥。使用wait()时,您需要拥有该对象上的监视器。所以在此之前你需要synchronized(..)。使用.wait()使当前线程停止,直到另一个线程在它等待的对象上调用.notify()。这是对synchronized的补充,它只确保只有一个线程进入块/方法。

答案 1 :(得分:7)

因此,在对此采访的一个问题上感到尴尬之后,我决定再次查看并理解第十亿次了。

synchronized块使代码线程安全。毫无疑问。当wait()和notify()或notifyAll()进来时,你试图编写更有效的代码。例如,如果您有多个线程共享的项目列表,那么如果您将它放在监视器的synchronized块中,那么线程线程将不断地跳入并在上下文切换期间来回运行代码,返回和堡垒..... 。甚至有一个空列表!

因此在监视器(synchronized(..)中的对象)上使用wait()作为一种机制,告诉所有线程放松并停止使用cpu循环,直到另行通知或notifyAll()。

如下所示:

synchronized(monitor) {

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


...somewhere else...

synchronized(monitor){

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

}

答案 2 :(得分:4)

将方法设为synchronized有两个影响:

  

首先,对同一对象的两个同步方法的调用不可能进行交错。当一个线程正在为一个对象执行一个synchronized方法时,所有其他线程为同一个对象块调用同步方法(暂停执行),直到第一个线程完成对象

其次,当synchronized方法退出时,它会自动与同一对象的同步方法的任何后续调用建立一个before-before关系。这可以保证对所有线程都可以看到对象状态的更改。

同步可帮助您保护关键代码。

如果您想在多个线程之间建立通信,则必须使用wait()notify() / notifyAll()

wait():使当前线程等到另一个线程调用此对象的notify()方法或notifyAll()方法。

notify():唤醒正在等待此对象监视器的单个线程。如果任何线程正在等待此对象,则选择其中一个线程被唤醒。

notifyAll():唤醒等待此对象监视器的所有线程。线程通过调用其中一个等待方法等待对象的监视器。

使用wait()和notify()的简单用例:生产者和消费者问题。

消费者线程必须等到Producer线程产生数据。 wait()和notify()在上面的场景中很有用。在一段时间内,引入了更好的替代方案。请参阅此high level concurrency教程页面。

简单来说:

使用synchronized来保护数据的关键部分并保护您的代码。

如果要以安全的方式在多个线程之间建立通信,并且彼此相互依赖,请使用wait()notify()以及同步。

相关的SE问题:

What does 'synchronized' mean?

A simple scenario using wait() and notify() in java

答案 3 :(得分:3)

有效的Java项目69:“鉴于使用wait和的困难 正确通知,您应该使用更高级别的并发实用程序。“

避免使用wait()和notify():尽可能使用synchronizedjava.util.concurrent中的其他实用程序。

答案 4 :(得分:0)

如果“同一对象”的2个线程试图获取锁,则使用同步块。由于对象类持有锁,它知道要给谁。 然而,如果2个对象的2个线程(比如说t2和t4)(obj1的t1和t2以及obj2的t3和t4)试图获取锁定,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。

答案 5 :(得分:0)

当您想要等待某些条件(例如用户输入) INSIDE 同步块时,

需要等待/通知。

典型用法:

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 保证立即恢复执行。如果有其他线程等待在同一个对象上执行同步块,那么唤醒线程应该与线程竞争。