如果我只使用synchronized,而不是wait / notify方法,它是否仍然保持线程安全?
有什么区别?
提前谢谢。答案 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问题:
答案 3 :(得分:3)
有效的Java项目69:“鉴于使用wait和的困难 正确通知,您应该使用更高级别的并发实用程序。“
避免使用wait()和notify():尽可能使用synchronized
或java.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)
需要等待/通知。
典型用法:
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 保证立即恢复执行。如果有其他线程等待在同一个对象上执行同步块,那么唤醒线程应该与线程竞争。