简体   繁体   English

带等待/通知和不带它们的同步块之间的区别?

[英]Difference between Synchronized block with wait/notify and without them?

If I just use synchronized , not the wait / notify methods, will it still be thread-safe?如果我只使用synchronized ,而不是wait / notify方法,它仍然是线程安全的吗?

What's the difference?有什么不同?

Using synchronized makes a method / block accessible by only on thread at a time.使用synchronized使一个方法/块一次只能在线程上访问。 So, yes, it's thread-safe.所以,是的,它是线程安全的。

The two concepts are combined, not mutually-exclusive.这两个概念是结合在一起的,而不是相互排斥的。 When you use wait() you need to own the monitor on that object.当您使用wait()您需要拥有该对象上的监视器。 So you need to have synchronized(..) on it before that.所以你需要在它之前synchronized(..) Using .wait() makes the current thread stop until another thread calls .notify() on the object it waits on.使用.wait()使当前线程停止,直到另一个线程对其等待的对象调用.notify() This is an addition to synchronized , which just ensures that only one thread will enter a block/method.这是对synchronized的补充,它只是确保只有一个线程会进入一个块/方法。

So after just being embarrassed in an interview question on this I decided to look it up and understand it again for 1 billionth time.所以刚刚在面试问题上尴尬之后,我决定再查一遍,第 10 亿次再理解一遍。

synchronized block makes the code thread safe. synchronized块使代码线程安全。 No doubt about that.毫无疑问。 When wait() and notify() or notifyAll() come in is where you are trying to write more efficient code.wait()notify()notifyAll()出现时,您将尝试编写更高效的代码。 For example, if you have a list of items that multiple threads share then if u put it in synchronized block of a monitor then threads threads will constantly jump in and run the code back and forth, back and forth during context switches......even with an empty list!例如,如果您有一个多个线程共享的项目列表,那么如果您将它放在监视器的synchronized块中,那么线程线程将不断跳入并在上下文切换期间来回来回运行代码...... ..即使有一个空列表!

The wait() is hence used on the monitor (the object inside the synchronized(..)) as a mechanism to to tell all threads to chill out and stop using cpu cycles until further notice or notifyAll().因此,在监视器(synchronized(..) 中的对象)上使用 wait() 作为一种机制,告诉所有线程冷静下来并停止使用 CPU 周期,直到进一步通知或 notifyAll()。

so something like:所以像:

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

...somewhere else... ...别的地方...

synchronized(monitor){
    list.add(stuff);
    monitor.notifyAll();
}

Making method as synchronized has two effects: 同步方法有两个作用:

First, it is not possible for two invocations of synchronized methods on the same object to interleave.首先,对同一对象的同步方法的两次调用不可能交错。 When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object当一个线程正在为一个对象执行同步方法时,所有其他调用同一个对象的同步方法的线程都会阻塞(挂起执行),直到第一个线程完成对对象的处理

Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object.其次,当一个同步方法退出时,它会自动建立一个发生在同一个对象的同步方法的任何后续调用之前的关系。 This guarantees that changes to the state of the object are visible to all threads.这保证了对象状态的更改对所有线程都是可见的。

synchronization help you to guard the critical code.同步帮助您保护关键代码。

If you want to establish communication between multiple threads, you have to use wait() and notify() / notifyAll()如果要在多个线程之间建立通信,必须使用wait()notify() / notifyAll()

wait() : Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. wait() :使当前线程等待,直到另一个线程为此对象调用 notify() 方法或 notifyAll() 方法。

notify() : Wakes up a single thread that is waiting on this object's monitor. notify() :唤醒在此对象的监视器上等待的单个线程。 If any threads are waiting on this object, one of them is chosen to be awakened.如果有任何线程正在等待该对象,则选择其中一个线程被唤醒。

notifyAll() :Wakes up all threads that are waiting on this object's monitor. notifyAll() :唤醒在此对象监视器上等待的所有线程。 A thread waits on an object's monitor by calling one of the wait methods.线程通过调用等待方法之一在对象的监视器上等待。

Simple use case for using wait() and notify() : Producer and Consumer problem .使用 wait() 和 notify() 的简单用例:生产者和消费者问题

Consumer thread has to wait till Producer thread produce data.消费者线程必须等到生产者线程产生数据。 wait() and notify() are useful in above scenario. wait() 和 notify() 在上述场景中很有用。 Over a period of time, better alternatives have been introduced.在一段时间内,已经引入了更好的替代方案。 Refer to this high level concurrency tutorial page.请参阅此高级并发教程页面。

In simple terms:简单来说:

Use synchronized to guard protect critical section of your data and guard your code.使用synchronized来保护数据的关键部分并保护您的代码。

Use wait() and notify() along with synchronization if you want to establish communication between multiple threads in safe manner, which are interdependent on each other.如果您想以安全的方式在相互依赖的多个线程之间建立通信,请使用wait()notify()以及同步。

Related SE questions:相关 SE 问题:

What does 'synchronized' mean? “同步”是什么意思?

A simple scenario using wait() and notify() in java java中使用wait()和notify()的简单场景

Effective Java item 69: " Given the difficulty of using wait and notify correctly, you should use the higher-level concurrency utilities instead ."有效的 Java 条款 69:“鉴于正确使用等待和通知的困难,您应该改用更高级别的并发实用程序。”

Avoid using wait() and notify(): use synchronized , or other utilities from java.util.concurrent , when possible.避免使用wait() 和notify():尽可能使用synchronizedjava.util.concurrent 中的其他实用程序。

Synchronised block is used, if 2 threads of "same object" tries to accquire the lock.如果“同一对象”的 2 个线程尝试获取锁,则使用同步块。 Since object class holds the lock, it knows who to give.由于对象类持有锁,它知道给谁。 Whereas, if 2 threads(say t2 and t4) of 2 objects( t1 & t2 of obj1 and t3 & t4 of obj 2) try to acquire the lock, obj1 would be unaware of obj2's lock and obj2 would be unaware of obj1's lock.然而,如果 2 个对象的 2 个线程(比如 t2 和 t4)(obj1 的 t1 & t2 和 obj 2 的 t3 & t4)尝试获取锁,obj1 将不知道 obj2 的锁,而 obj2 将不知道 obj1 的锁。 Hence wait and notify methods are used.因此使用了等待和通知方法。

eg:例如:

//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();  
}  
} 

Two threads t1 and t2 belongs to same object, hence synchronization works fine here.两个线程 t1 和 t2 属于同一个对象,因此同步在这里工作正常。 Whereas,然而,

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();  
}  
} 

When you run the above program, synchronisation does not work since each thread belong to different object, Hence you should use wait and notify here.当您运行上述程序时,由于每个线程属于不同的对象,因此同步不起作用,因此您应该在这里使用等待和通知。

wait/notify is required when you want to wait for some condition (eg user input) INSIDE a synchronized block.当你想等待一些条件(例如,用户输入)的同步块等待/通知是必需的。

Typical usage:典型用法:

synchronized(obj) {
    // do something

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

Let's assume that you don't use wait().假设您不使用wait()。 Then, you have to implement busy loop polling the condition that you want, which is bad for performance.然后,您必须实现繁忙循环轮询所需的条件,这对性能不利。

synchronized(obj) {
    // do something

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

    // do something other
}

Important note: Even though a thread is awaken by notify() or notifyAll() from other thread, the awaken thread does NOT guaranteed to immediately resume its execution.重要提示:即使一个线程被其他线程的 notify() 或 notifyAll() 唤醒,唤醒线程也不能保证立即恢复执行。 If there were other threads awaiting to execute a synchronized block on the same object, then the awaken thread should compete with the threads.如果有其他线程等待在同一对象上执行同步块,则唤醒线程应与线程竞争。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM