java—在实现可运行接口时导致死锁,在扩展线程类时运行良好

hfyxw5xn  于 2021-07-09  发布在  Java
关注(0)|答案(2)|浏览(262)

为了理解wait()和notifyall()的功能,我创建了一个程序来更好地理解前面提到的多线程概念。但是不知怎么的,我注意到下面的程序在通过线程类工作并产生预期的输出时运行良好。但是当我用runnable接口切换同一个程序时,我面临死锁问题。
任何关于我错在哪里或者我对这个概念缺乏什么理解的建议。

class Test{
  public static void main(String[] args) {
    Notifier notifier = new Notifier();
    new Waiter(notifier, "A").start();
    new Waiter(notifier, "B").start();

    notifier.start();
  }
}

在不同线程上调用wait()的服务程序类。

class Waiter extends Thread{

  private Notifier notifier;

  Waiter(Notifier notifier, String name){
    super(name);
    this.notifier = notifier;
  }

  public void run(){
    synchronized(notifier){
      System.out.println(Thread.currentThread().getName() + " is trying to call wait()");
      try{
        notifier.wait();
      }catch(InterruptedException e){}
       System.out.println(Thread.currentThread().getName() + " will now continue with remaining code");
    }
  }
}

通知程序类调用notifyall()并通知所有等待的线程。

class Notifier extends Thread{
  public void run(){
    synchronized(this){
      try{
        Thread.sleep(100);
      }catch(InterruptedException e){}
        System.out.println("Notifying all threads");
        notifyAll();
    }
  }
}

以下程序的o/p为

A is trying to call wait()
B is trying to call wait()
Notifying all threads
B will now continue with remaining code
A will now continue with remaining code

现在相同的程序使用runnable接口

class Test{
  public static void main(String[] args) {
    Notifier notifier = new Notifier();
    // new Waiter(notifier, "A").start();
    // new Waiter(notifier, "B").start();
    //
    // notifier.start();

    new Thread(new Waiter(notifier), "A").start();
    new Thread(new Waiter(notifier), "B").start();

    new Thread(notifier).start();
  }
}

class Waiter implements Runnable{

  private Notifier notifier;

  Waiter(Notifier notifier){
    // super(name);
    this.notifier = notifier;
  }

  public void run(){
    synchronized(notifier){
      System.out.println(Thread.currentThread().getName() + " is trying to call wait()");
      try{
        notifier.wait();
      }catch(InterruptedException e){}
       System.out.println(Thread.currentThread().getName() + " will now continue with remaining code");

    }
  }
}

class Notifier implements Runnable{
  public void run(){
    synchronized(this){
      try{
        Thread.sleep(100);
      }catch(InterruptedException e){}
        System.out.println("Notifying all threads");
        notifyAll();
    }
  }
}

以下程序的o/p并导致死锁情况

A is trying to call wait()
Notifying all threads
B is trying to call wait()
A will now continue with remaining code

我尝试在1毫秒后启动通知程序线程,使主线程休眠1毫秒并获得所需的输出。

new Thread(new Waiter(notifier), "A").start();
    new Thread(new Waiter(notifier), "B").start();

     try{
       Thread.sleep(1);
     }catch(InterruptedException e){}

    new Thread(notifier).start();

但是为什么在正常/顺序启动通知程序线程(即不睡眠)时它不工作。是因为我正在为可运行的目的创建额外的对象还是其他原因?

d6kp6zgx

d6kp6zgx1#

我已经找到了没有达到预期效果的原因。这是因为我正在对通知程序对象执行操作。现在,当我们在程序中启动几个线程时,它们会随机执行,而不是按顺序执行,因为线程调度程序(jvm的一部分)决定此时应该运行哪个线程,而不是按线程的顺序运行。
此外,每个对象都有一个唯一的锁。在对任何对象调用wait()、notify()和notifyall()时,线程应该是该对象的所有者,即线程必须拥有该对象的锁。现在,当一个线程(在我们的例子中是服务线程)调用某个对象的wait()时(在我们的例子中,notifier就是这个对象),

class Waiter implements Runnable{

  private Notifier notifier;

  Waiter(Notifier notifier){
    this.notifier = notifier;
  }

  public void run(){
    synchronized(notifier){
      System.out.println(Thread.currentThread().getName() + " is trying to call wait()");
      try{
        notifier.wait();
      }catch(InterruptedException e){}
       System.out.println(Thread.currentThread().getName() + " will now continue with remaining code");

    }
  }
}

然后该线程立即释放锁并进入等待状态,以便其他线程可以处理该对象。现在,假设通知程序线程有机会运行(由线程调度程序提供)并获得锁。

class Notifier implements Runnable{
  public void run(){
    synchronized(this){
      try{
        Thread.sleep(100);
      }catch(InterruptedException e){}
        System.out.println("Notifying all threads");
        notifyAll();
    }
  }

现在,在完成它的任务之后,它(通知线程)通知所有线程(通过调用notifyall())并释放锁,以便其他线程(服务线程)可以继续它们的进一步执行。当其他服务程序线程开始执行时,它们进入synchronized block并调用wait(),它们必须等待终生(或者直到我们异常退出程序),因为没有人通知它们,因为通知程序线程已经执行,这会导致死锁情况。
现在,如果我们在一段时间(毫秒)之后启动通知程序线程,那么这个问题就可以得到解决,这样所有线程(服务员线程)就可以在通知程序线程启动并生成所需的输出之前,一个接一个地开始对通知程序对象调用wait()。

new Thread(new Waiter(notifier), "A").start();
  new Thread(new Waiter(notifier), "B").start();

   try{
     Thread.sleep(1);
   }catch(InterruptedException e){}

  new Thread(notifier).start();

下面是固定程序

class Test{
  public static void main(String[] args) {
    Notifier notifier = new Notifier();
    new Thread(new Waiter(notifier), "A").start();
    new Thread(new Waiter(notifier), "B").start();

    try{
      Thread.sleep(1);
    }catch(InterruptedException e){}

    new Thread(notifier, "Notifier").start();
  }
}

class Waiter implements Runnable{

  private Notifier notifier;

  Waiter(Notifier notifier){
    this.notifier = notifier;
  }

  public void run(){
    synchronized(notifier){
      System.out.println(Thread.currentThread().getName() + " is trying to call wait()");
      try{
        notifier.wait();
      }catch(InterruptedException e){}
        System.out.println(Thread.currentThread().getName() + " will now continue with remaining code");
      }
    }
  }

  class Notifier implements Runnable{
    public void run(){
      synchronized(this){
        try{
          Thread.sleep(2000);
        }catch(InterruptedException e){}
          System.out.println("Notifying all threads");
          notifyAll();
        }
      }
    }

以及所需的输出:

A is trying to call wait()
B is trying to call wait()
Notifying all threads
B will now continue with remaining code
A will now continue with remaining code
brtdzjyr

brtdzjyr2#

您的代码有一个典型的竞争条件问题。你们两个例子都很相似。如果 Thread 有名字吗 Notifier 会比线程快 A 或者 B 你会得到 DeadLock 对于有用法的代码 Thread 扩展和编码 Runnable 实施。
如果你的 Notifier 将是最后一个你的代码将成功的两个例子。您可以多次运行代码并在这种情况下重现所有代码。
你有很多解决这个问题的方法,我写了其中一种,有两个倒计时锁。在本例中,我们将有一个严格定义的线程执行序列:

public class Solution0 {

    public static void main(String[] args) throws FileNotFoundException, InterruptedException {
        CountDownLatch countDownLatch1 = new CountDownLatch(1);
        CountDownLatch countDownLatch2 = new CountDownLatch(1);
        new Waiter(countDownLatch1, "A").start();
        while (countDownLatch1.getCount() != 0) {
            Thread.sleep(500);
        }
        new Waiter(countDownLatch2, "B").start();
        while (countDownLatch2.getCount() != 0) {
            Thread.sleep(500);
        }

        Notifier notifier = new Notifier();
        notifier.start();
    }
}

class Waiter extends Thread {

    private final CountDownLatch countDownLatch;

    Waiter(CountDownLatch countDownLatch, String name) {
        super(name);
        this.countDownLatch = countDownLatch;
    }

    public void run() {
        System.out.println(Thread.currentThread().getName() + " is trying to call wait()");
        countDownLatch.countDown();
    }
}

class Notifier extends Thread {

    public synchronized void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        System.out.println("Notifying all threads");
        notifyAll();
    }
}

相关问题