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

Posted

技术标签:

【中文标题】带有等待/通知和没有它们的同步块之间的区别?【英文标题】:Difference between Synchronized block with wait/notify and without them? 【发布时间】:2011-12-12 03:19:19 【问题描述】:

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

有什么区别?

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

那么为什么我们需要使用等待/通知方法呢?一定有一些不同吧? 我认为当同步块结束时,它会释放锁。其他执行同步方法或语句的线程在无法获得锁时会阻塞。它的作用也类似于 wait() notify() 机制,非常相似。 Alan 在问 wait() 和 notify() 与普通同步块结束有什么区别。【参考方案2】:

因此,在一次面试问题中感到尴尬之后,我决定再次查找并理解它的第 10 亿次。

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

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

类似:

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

...其他地方...

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

【讨论】:

规定你总是在循环中调用 wait() ,例如while ( list.isEmpty() ) monitor.wait() 如果您想等到另一个线程实际上将某些内容放入列表中。 docs.oracle.com/javase/7/docs/api/java/lang/Object.html#wait() 这应该是答案。程序员不能选择先执行哪个同步块。但是等待/通知可以指导哪个块应该先去。 +1。【参考方案3】:

将方法设为synchronized有两个作用:

首先,同一对象上的同步方法的两次调用不可能交错。当一个线程正在为一个对象执行同步方法时,所有其他为同一对象调用同步方法的线程都会阻塞(暂停执行),直到第一个线程处理完该对象

其次,当一个同步方法退出时,它会自动与任何后续对同一对象的同步方法调用建立起之前的关系。这保证了对象状态的更改对所有线程都是可见的。

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

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

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

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

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

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

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

简单来说:

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

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

相关的 SE 问题:

What does 'synchronized' mean?

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

【讨论】:

【参考方案4】:

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

避免使用 wait() 和 notify():尽可能使用 synchronized 或来自 java.util.concurrent 的其他实用程序。

【讨论】:

【参考方案5】:

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

当你运行上述程序时,由于每个线程属于不同的对象,同步不起作用,因此你应该在这里使用等待和通知。

【讨论】:

【参考方案6】:

当您想要等待某些条件(例如用户输入)时,需要等待/通知 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() 唤醒,被唤醒的线程保证立即恢复执行。如果有其他线程等待对同一对象执行同步块,则唤醒线程应该与线程竞争。

【讨论】:

我不确定这是一个很好的例子。 wait() 是一种阻塞方法,因此它不必在无限循环中。您可以简单地在一个同步块中使用 wait(),当满足您的条件时,您可以在另一个同步块中使用 notify() 来“解除阻塞”wait() 方法。

以上是关于带有等待/通知和没有它们的同步块之间的区别?的主要内容,如果未能解决你的问题,请参考以下文章

为什么wait(),notify()和notifyAll()必须在同步块或同步方法中调

(转)为什么wait(),notify()和notifyAll()必须在同步块或同步方法中调用

Java中的同步方法和同步块有啥区别? [复制]

Synchronized与ReentrantLock的区别

java 面试题

Synchronized与ReentrantLock的区别