何时调用Object.wait - java

当我们有一个线程正在执行同步块时,还有另一个线程试图访问该同步块。

是否会在阻塞的线程上自动调用Object.wait?
另外,我看到在Object类中,wait的定义是:

public final native void wait(long timeout) throws InterruptedException;

这是否意味着我们必须在类中手动编写类似下面的函数。我看过很多例子:

public void doWait(){
    synchronized(obj){
      while(!wasSignalled){
        try{
          obj.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }

public void doNotify(){
    synchronized(obj){
      wasSignalled = true;
      obj.notify();
    }
  }

参考方案

不,不会调用Object::waitwait / notify机制是synchronized提供的基本锁定之上的另一层;无需使用synchronizedwait即可使用notify

基本的synchronized机制基于锁定和解锁附加到特定对象的锁(该锁有时称为监视器)的思想。如果一个线程锁定了该锁,则另一个试图锁定它的线程将阻塞。当第一个线程解锁该锁时,第二个线程解锁,并继续锁定该锁。

wait / notify机制为线程提供了一种暂时放弃其持有的锁的方法,该锁的重新获得由与此同时持有该锁的某个其他线程控制。考虑以下代码:

public synchronized void first() {
    System.out.println("first before");
    wait();
    System.out.println("first after");
}

public synchronized void second() {
    System.out.println("second before");
    notify();
    System.out.println("second after");
}

说一个线程,线程A调用first,然后另一个线程B调用second。事件的顺序是:

尝试获取锁
一个成功获得锁
一个去写“先于”到System.out
B尝试获取锁
B无法获得锁,因为A拥有它,所以它会阻塞
A完成写入,并调用wait-此时A释放了锁,而是开始等待
B现在成功获取了锁,并且解除了锁定
B去写“ System.out之前”
B完成写入,并调用notify-这对B没有影响,但是这意味着A停止等待并尝试重新获取锁
A无法获得锁,因为B拥有它,所以它会阻塞
B去写“ second after”到System.out
B完成方法,然后释放锁
现在,A成功获取了锁,并且解除了锁定
A进行并将“ first after”写入System.out
完成方法,然后释放锁

这是一个冗长的描述,但这实际上是一个非常简单的过程-wait / notify调用有点让第一个线程将锁借给另一个线程使用。

重要的是要意识到存在两种不同类型的阻塞。首先,线程在进入synchronized块时阻塞等待获取锁的方式(或在wait调用返回时重新进入锁)。其次,线程在调用wait之后被相应的notify取消阻止之前的阻止方式。

我将wait / notify描述为一个线程借给另一个锁。这就是我的想法,我认为这是一个富有成效的隐喻。用一个局部怪异的比喻,也许就像一个吸血鬼走进城堡,然后睡在棺材里。一旦他睡着了,一些无辜的游客就会进来并将城堡出租作为度假屋。在某个时候,游客参观了地下室并打扰了棺材,这时吸血鬼醒来并要他的城堡归还。一旦游客惊恐逃离,他就可以搬回房子。

waitnotify具有其名称的原因,而不是诸如lendreturn之类的名称,是因为它们通常用于建立线程间通信机制,其中重点不在初始贷款上通过第一个线程锁定,但是在第二个线程唤醒服务员时。

现在,最后转到您的第二个问题,需要考虑两件事。

第一个是“虚假唤醒”的可能性-请参阅Java语言规范17.2.1. Wait部分中嵌套项目符号列表中的小注释:

可能由于实现的内部动作而将线程从等待集中删除。尽管不鼓励这样做,但允许执行“虚假唤醒”的实现,即从等待集中删除线程,从而无需明确的指示即可进行恢复。

也就是说,线程通常只会在收到通知时唤醒,但是有可能在没有通知的情况下随机唤醒。因此,您确实需要使用涉及检查条件变量的循环来保护wait,这与您的示例完全相同。如规范所述:

注意,此规定需要Java编码实践,即仅在线程正在等待的某些逻辑条件成立时才终止的循环内使用wait。

第二个是打扰。中断不是随机的;仅当其他线程在正在等待的线程上调用interrupt时,才会发生中断。发生这种情况时,它将立即停止阻塞,并从InterruptedException调用中抛出wait。与您所看到的相反,捕获此异常并再次等待是不正确的。原因很简单:如果有人在您的线程上调用了interrupt,那恰恰是因为他们想要您stop waiting!无法确切地说出线程应该做什么,但是通常的方法是中止当前工作,并将控制权返回给调用者。如果在中止当前工作之后调用者无法继续,则它也应中止,依此类推,直到调用堆栈达到可以做一些明智的事情为止。正确处理中断的内容在这里无法解决,但请先阅读本教程中关于Supporting Interruption的内容,如果可能,请阅读Java Concurrency In Practice。

等待和等待时间差异? - java

我在一次采访中遇到了这个问题。线程中的wait和wait on time有什么区别?我知道wait方法使当前线程等待,直到另一个线程为此对象调用notify()方法或notifyAll()方法,或者经过了指定的时间。但是我不确定他在问什么准时。谁能解释一下等待时间是什么意思?提前致谢。 参考方案 它们可能表示Object.wait(long timeout)…

为什么“通知”唤醒所有正在等待的线程,尽管应该只影响一个线程? - java

在以下代码中,两个使用者线程启动并等待。生产者线程在那之后启动(很有可能)并调用“ notify”。所有线程都将生产者用作监视器。Thread producer = new Thread() { @Override public void run() { synchronized (this) { System.out.printf("notify…

java:继承 - java

有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…

Java:BigInteger,如何通过OutputStream编写它 - java

我想将BigInteger写入文件。做这个的最好方式是什么。当然,我想从输入流中读取(使用程序,而不是人工)。我必须使用ObjectOutputStream还是有更好的方法?目的是使用尽可能少的字节。谢谢马丁 参考方案 Java序列化(ObjectOutputStream / ObjectInputStream)是将对象序列化为八位字节序列的一种通用方法。但…

Java-如何将此字符串转换为日期? - java

我从服务器收到此消息,我不明白T和Z的含义,2012-08-24T09:59:59Z将此字符串转换为Date对象的正确SimpleDateFormat模式是什么? java大神给出的解决方案 这是ISO 8601标准。您可以使用SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM…