当前位置: 首页 > 知识库问答 >
问题:

在同一个监视器上等待的两个线程可以称为死锁吗?

姜振濂
2023-03-14

两个线程在同一个监视器上等待,例如,如果一个线程调用等待锁,而另一个获取监视器的线程在通知第一个线程之前也调用等待。现在两个线程都在等待,但是没有人得到通知。我该怎么称呼这种情况?这能叫僵局吗?

编辑:假设只有这两个线程,并且无法从其他地方通知它们
更新:我刚刚创建了我在问题中描述的情况。当转换器线程在侦听器线程之前启动时,以下代码大部分时间都正常工作。然而,当我在changer之前启动listener时,程序在打印两行(一行来自changer,另一行来自listener线程)后挂起。我在更改之前调用侦听器的情况会被称为死锁吗

package demo;

public class ProducerConsumer {

public static int SAMPLE_INT = 0;

public static void main(String[] args) {

    PC pc = new PC();

     Thread changer = new Thread(new Runnable() {
        public void run(){
            try {
                pc.producer();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
     });

    Thread listener = new Thread(new Runnable(){
        public void run() {
            try {
                pc.consumer();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    changer.start();
    listener.start(); 
   }
 }

class PC {

Object lock = new Object();

public void producer() throws InterruptedException {
    synchronized(this){
        for (int i=0; i<5; i++){
            ProducerConsumer.SAMPLE_INT++;
            System.out.println("Changed value of int to: " + ProducerConsumer.SAMPLE_INT);
            wait();
            notify();
           }
    }
}

public void consumer() throws InterruptedException{
    synchronized(this){
        for (int i=0; i<5; i++){
            System.out.println("Receieved Change: " + ProducerConsumer.SAMPLE_INT);
            notify();
            wait();
           }
         }
       }
     }


在侦听器之前启动转换器时输出:
将int值更改为:1
接收的更改:1
将int值更改为:2
接收的更改:2
将int值更改为:3
接收的更改:3
将int值更改为:4
将int值更改为:5
接收的更改:5
程序终止

在转换器之前启动侦听器时的输出:
接收到的更改:0
将int的值更改为:1
程序不会终止。

谢谢

共有3个答案

顾宏朗
2023-03-14

死锁基本上是指一个线程持有一个锁(第一个锁),然后想要获得另一个锁(第二个锁),但它永远无法获得,因为第二个锁被另一个想要获得第一个锁的线程持有。这也可能发生在线程链中,例如,Thread1有LockA,Thread2有LockB,Thread3有LockC,并且它们正在等待其他线程持有的锁(例如,Thread1想要LockB,Thread2想要LockC,而Thread3想要LockA)。在这种情况下,没有一个线程可以继续。

只有这种情况才被称为死锁;持有一个锁的线程等待另一个锁,除非释放它的锁,否则该锁永远不会被获取。

对lock对象调用wait,从技术上释放线程所持有的锁。

所以,为了回答你的问题,我认为你不能把问题中提到的场景称为死锁。

令狐嘉禧
2023-03-14

这不是死锁,因为有办法摆脱这种情况——如果第三个线程调用通知()通知所有(),那么前两个等待线程将返回到就绪状态。

死锁通常无法在应用程序本身内解决,需要重新启动。

这就是为什么我不会称你的处境为僵局。

还有两个术语描述线程协调问题:

生存与饥饿

下面是LoveLock和饥饿的确切定义——饥饿和活锁

这也不是一个活锁,因为线程之间没有响应。

你的处境可能与饥饿这个词最接近,但并不完全是饥饿。饥饿是指线程等待占用了很长时间的资源。在您的情况下,资源是对象的锁,它将永远不会被再次获取。所以你最好的办法就是“无休止的饥饿”。

我个人认为这是生产者-消费者错误,因为等待通知机制描述和管理生产者-消费者模式的线程协调,这种方法(没有通知的等待)只是开发人员错误或方法的误用。

鲁丰
2023-03-14

如果仅涉及这两个线程(如访问监视器),则为是。如果有其他线程可以访问监视器并将其解锁,则没有。

但是请记住,您讨论的是两个主题—监视器通常是线程术语中的互斥体。但是,wait是一种与条件变量相关的东西,它在要求互斥锁工作的同时,执行一项更微妙的任务,根据一个线程向另一个线程发出警告信号的条件故意阻塞线程,这种情况称为虚假唤醒。

 类似资料:
  • 问题内容: 当前,我们正在分析tomcat线程转储。在tomcat上同时运行的所有线程的单线程转储包含以下行: 特别是我们不明白 根据我们的理解,它说三个线程当时正在锁定同一监视器。根据我们的理解,根据JLS,这是不可能的。 我们对线程转储的解释正确吗? 问题答案: 看起来所有这些线程都在等待与监视器关联的条件,即它们称为该监视器的方法。 当线程在其拥有的监视器上调用时,它会临时释放监视器,并在从

  • 我做了几个线程转储,发现有16个线程在等待同一个锁,例如: “__ejb-thread-pool1”守护进程prio=6 tid=0x39657c00 nid=0x1c08在条件[0x3297f000]java.lang.thread.state:waiting(parking)在sun.misc.unsafe.park(本机方法)-在java.util.concurrent.locks.lock

  • 我写了一个启动两个线程的代码片段;一个线程打印所有奇数,而另一个线程打印所有偶数。我使用了内在锁和线程通信命令的组合来实现两个线程的正确交叉。这是我的代码, 以下是我的问题: > 奇数线程在printOdd()函数中执行,而偶数线程在print偶数()函数中执行。我对两个线程都使用一个内在锁;我不明白两个线程怎么能同时存在于各自的同步块中,因为使用了相同的锁。 我从代码中删除了线程通信语句(通知,

  • 问题内容: 我一直在弄乱Java中的线程来处理它们(这似乎是最好的方法),现在了解了sync,wait()和notify()的情况。 我很好奇是否有一种方法可以同时对两个资源进行wait()。我认为以下内容并不能完全满足我的想法( 编辑 : 请注意,此示例中省略了通常的while循环,仅专注于释放两个资源 ): 在这种(非常人为)情况下,将保留token2直到返回token1,然后将保留token

  • 问题内容: 工作中的某个人只是问了要在同步对象中包装等待的原因。 老实说,我看不出推理的原因。我了解javadocs所说的内容- 该线程需要成为对象监视器的所有者,但是为什么呢?它可以防止什么问题?(如果确实有必要,为什么wait方法不能获取监视器本身?) 我正在寻找一个相当深入的原因,或者可能是一篇文章的参考。我无法在快速的Google中找到一个。 哦,还有,thread.sleep比较起来如何

  • 我如何启动两个线程,其中thread1首先执行,thread2在thread1结束时启动,而主方法线程可以在不锁定其他两个线程的情况下继续工作? 我尝试了join(),但是它需要从线程调用,线程必须等待另一个线程,没有办法执行类似thread2.join(thread1)的操作;因此,如果我在main()中调用join,我将有效地停止主线程的执行,而不仅仅是Thread2的执行。 #编辑:为什么我