【问题标题】:Does Sychronized block on same object really hold other thread from executing the block?同一个对象上的同步块是否真的阻止其他线程执行该块?
【发布时间】:2014-03-03 12:49:22
【问题描述】:

嗨,我试图理解等待通知,我在代码中看到了这种行为,即 2 个线程正在同一对象的同步块内打印语句。

public class WaitNotifyExample {

/**
 * @param args
 */
public static void main(String[] args) {
    Message msg = new Message("process it");
    Waiter waiter = new Waiter(msg);
    new Thread(waiter,"waiter").start();

    Waiter waiter1 = new Waiter(msg);
    new Thread(waiter1, "waiter1").start();

    Notifier notifier = new Notifier(msg);
    new Thread(notifier, "notifier").start();
    //System.out.println("All the threads are started");

}

}

class Message {
private String msg;

public Message(String str){
    this.msg=str;
}

public String getMsg() {
    return msg;
}

public void setMsg(String str) {
    this.msg=str;
}

}

class Waiter implements Runnable{

private Message msg;

public Waiter(Message m){
    this.msg=m;
}

@Override
public void run() {
    String name = Thread.currentThread().getName();
    synchronized (msg) {
        try{
            System.out.println(name+" waiting to get notified at time:"+System.currentTimeMillis());
            msg.wait();
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(name+" waiter thread got notified at time:"+System.currentTimeMillis());
        //process the message now
        System.out.println(name+" processed: "+msg.getMsg());
    }
}

}
class Notifier implements Runnable {

private Message msg;

public Notifier(Message msg) {
    this.msg = msg;
}

@Override
public void run() {
    String name = Thread.currentThread().getName();
    System.out.println(name+" started");
    try {
        Thread.sleep(1000);
        synchronized (msg) {
            msg.setMsg(name+" Notifier work done");
            //msg.notify();
            msg.notifyAll();
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

}

这是程序的输出:

- 等待时间通知的服务员:1393849891481 -通知程序已启动 -waiter1 等待收到通知的时间:1393849891483 -waiter1 服务员线程在时间得到通知:1393849892483 -waiter1 已处理:通知程序通知程序工作已完成 -waiter 服务员线程在时间得到通知:1393849892483 服务员处理:通知程序通知程序工作完成

【问题讨论】:

    标签: java multithreading wait synchronized


    【解决方案1】:

    阅读Object.wait上的Javadoc:

    线程释放此监视器的所有权并等待,直到另一个线程通知在此对象的监视器上等待的线程

    当线程在msg.wait 中被阻塞时,它不拥有msg 的监视器。任何其他线程都可以免费获取它。

    【讨论】:

      【解决方案2】:

      代码按预期工作:没有单个线程干扰消息的输出,否则(如果流未在内部同步)这些字母和单词将完全混淆并打印出乱码。

      然而,一旦你调用了wait,你就会暂停当前线程并释放监视器上的锁,这样其他线程就可以同时打印一些东西。

      您的代码还很好地展示了另一件事:由于未定义线程的计时,因此在调用 notifyAll() 时,实际上可能有一个、多个或零个线程在等待。这是常见的等待/通知问题之一,如果线程 B 已经在线程 A 等待之前调用了 notify

      另外请注意,同步/等待/通知是一种非常基本的“蛮力”线程同步方法,它带有许多陷阱,而且速度也不是很快,因为许多线程通常被搁置。它可以用于基本和简单的代码,但如果你真的想深入研究线程,你不应该使用它,而是使用concurrent package 和它的类和特性。例如,可以使用LinkedBlockingQueue<String> 而不是使用同步来编写相同的代码。

      【讨论】:

      • 谢谢 TwoThe,我同意你的观点,我是并发包的忠实粉丝,但是我参加面试的很多公司,他们不知道并发包并坚持我写 wait(),通知(),通知所有()
      【解决方案3】:

      好的。我认为重要的一点已经涵盖,即 wait() 释放监视器/锁,以便其他线程可以进入同步块。但我想补充两个重点。 1. 使用 wait() 时不要忘记检查您应该等待的时间或时间。您可能会成为虚假唤醒电话的受害者。检查此链接 http://handling-thread.blogspot.co.uk/2012/11/what-is-spurious-wakeup-while-wait-in.html 2.如果您正在考虑等待通知方法,我建议您使用使用 Condition.await-signal 的相同方法的最新解决方案

      供您参考,我已按条件方法更新了代码。

      import java.util.concurrent.locks.Condition;
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      
      public class WaitNotifyExample {
      
          /**
           * @param args
           */
          public static void main(String[] args) {
              Message msg = new Message("process it");
              Waiter waiter = new Waiter(msg);
              new Thread(waiter, "waiter").start();
      
              Waiter waiter1 = new Waiter(msg);
              new Thread(waiter1, "waiter1").start();
      
              Notifier notifier = new Notifier(msg);
              new Thread(notifier, "notifier").start();
              //System.out.println("All the threads are started");
      
          }
      
      }
      
      class Message {
          final Lock lock = new ReentrantLock();
          final Condition msgAvailable = lock.newCondition();
      
          private String msg = null;
      
          public Message(String str) {
              this.msg = str;
          }
      
          public String getMsg() {
              return msg;
          }
      
          public void setMsg(String str) {
              this.msg = str;
          }
      
          public void lock() {
              lock.lock();
          }
      
          public void await() throws InterruptedException {
              msgAvailable.await();
          }
      
          public void unlock() {
              lock.unlock();
          }
      
          public void signal() {
              msgAvailable.signal();
          }
      
          public void signalAll() {
              msgAvailable.signalAll();
          }
      }
      
      class Waiter implements Runnable {
      
          private Message msg;
      
      
      
          public Waiter(Message m) {
              this.msg = m;
          }
      
          @Override
          public void run() {
              String name = Thread.currentThread().getName();
              msg.lock();
                  try {
                      System.out.println(name + " waiting to get notified at time:" + System.currentTimeMillis() + "   Object: " + msg);
                      //You missed while condition which is very important aspect of wait-notify. You can check this link
                      //http://handling-thread.blogspot.co.uk/2012/11/what-is-spurious-wakeup-while-wait-in.html
                      while(msg.getMsg() == null)
                          msg.await();
                      //msg.wait();
                      System.out.println(name + " waiter thread got notified at time:" + System.currentTimeMillis() + "   Object: " + msg);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  } finally {
                      msg.unlock();
                  }
                  //process the message now
                  System.out.println(name + " processed: " + msg.getMsg());
              }
          }
      
      class Notifier implements Runnable {
      
          private Message msg;
      
          public Notifier(Message msg) {
              this.msg = msg;
          }
      
          @Override
          public void run() {
              String name = Thread.currentThread().getName();
              System.out.println(name + " started");
              msg.lock();
              try {
                  Thread.sleep(1000);
                      msg.setMsg(name + " Notifier work done");
                      //msg.notify();
                      msg.signalAll();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }finally {
                  msg.unlock();
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-05
        • 2017-04-20
        • 2019-02-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-11
        • 2021-08-03
        相关资源
        最近更新 更多