【问题标题】:Java waiting Thread doesn't resume after notify通知后Java等待线程不恢复
【发布时间】:2017-06-09 08:55:06
【问题描述】:

我有下面的程序有 2 个线程 T1 和 T2。 T1 先运行并进入等待状态。 T2 正在调用通知。为什么 T1 线程不恢复并打印“线程 0 已唤醒”

public class WaitNotifyTest{
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    System.out.println(Thread.currentThread().getName() + " is running");
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " is waken up");
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    try {
                        Thread.sleep(3000);
                        System.out.println(Thread.currentThread().getName() + " is running");
                        notify();
                        System.out.println(Thread.currentThread().getName() + " notifying");
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        });
        t1.start();
        t2.start();
    }
}

输出为

Thread-0 is running
Thread-1 is running
Thread-1 notifying

请注意,此输出后我的程序不会结束/终止。谁能解释一下为什么我的程序没有终止。

【问题讨论】:

  • 您可能需要notifyAll() 而不是通知notifyAll vs notify
  • 不。我试过这个。它给出了相同的结果。什么都没有改变。

标签: java multithreading wait notify


【解决方案1】:

您的代码中有两个问题 1.您应该使用相同的对象在线程之间进行通信。 2.调用等待并通知您已锁定的同一对象。

public class WaitNotifyTest{


    public static void main(String[] args) {
        Object lock=new Object(); 
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        System.out.println(Thread.currentThread().getName() + " is running");
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + " is waken up");
                    }
                }
            });
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        try {
                            Thread.sleep(3000);
                            System.out.println(Thread.currentThread().getName() + " is running");
                            lock.notify();
                            System.out.println(Thread.currentThread().getName() + " notifying");
                        } catch (InterruptedException e1) {
                            e1.printStackTrace();
                        }
                    }
                }
            });
            t1.start();
            t2.start();
        }
    }

【讨论】:

    【解决方案2】:

    我认为这是由于 synchronized (this) 被用于锁定目的。

    在 t1 中,this 指的是在那里创建的匿名类的 Object。

    在 t2 中,this 将引用在此处创建的另一个匿名类的 Object。简单来说,两者都指代不同的对象。

    要让wait-notify 工作,您必须锁定同一个对象。

    【讨论】:

    • 我正在对其进行测试和调试。在这两个线程中,这指的是 WaitNotifyTest 类的对象。
    • @PradeepSingh 不是兄弟,他们指的是两个不同的对象。正如您在其他评论中写的org.A.WaitNotifyTest$1@2b4ebf39 和其他org.A.WaitNotifyTest$2@57c44c58。注意 Test$1 和 Test$2。这两个是不同的对象。
    • @gprathour 是对的。它们指的是Runnable 的两个不同实例。毕竟,您在使用new 创建的两个不同对象中使用了this。它们怎么可能一样?
    【解决方案3】:

    问题是您没有在同一个对象上同步。尝试在每个线程中打印this 以进行验证。然后尝试在同一个对象上同步,在该锁上调用waitnotify(否则您将不会得到正确的响应)。

       public class WaitNotifyTest{
        public static void main(String[] args) {
            Integer someObject = 2;
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("t1 this = " + this);
                    synchronized (someObject) {
                        System.out.println(Thread.currentThread().getName() + " is running");
                        try {
                            someObject.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + " is waken up");
                    }
                }
            });
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("t2 this = " + this);
                    synchronized (someObject) {
                        try {
                            Thread.sleep(3000);
                            System.out.println(Thread.currentThread().getName() + " is running");
                            someObject.notify();
                            System.out.println(Thread.currentThread().getName() + " notifying");
                        } catch (InterruptedException e1) {
                            e1.printStackTrace();
                        }
                    }
                }
            });
            t1.start();
            t2.start();
        }
    }
    

    【讨论】:

    • 我试过了。这两个都是指同一个对象。即 WaitNotifyTest$1@2b4ebf39
    • 嗯,对我来说,情况并非如此:类相同,但实例编号不同。
    • 举个例子,输出我上面发布的代码(我把类放在包里singleuse:t1 this = singleuse.WaitNotifyTest$1@49b8ef40t2 this = singleuse.WaitNotifyTest$2@40bea1f
    【解决方案4】:

    您没有正确使用waitnotify

    您必须在同一个对象上同步,以便线程能够等待通知彼此。 您可以在它们之间共享一个对象并调用 waitnotify 与同步块使用相同的共享 Object 作为监视器。下面解决了这个问题:

    public static void main(String[] args) {
        Object obj = new Object();
    
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (obj) {
                    System.out.println(Thread.currentThread().getName() + " is running");
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " is waken up");
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (obj) {
                    try {
                        Thread.sleep(3000);
                        System.out.println(Thread.currentThread().getName() + " is running");
                        obj.notify();
                        System.out.println(Thread.currentThread().getName() + " notifying");
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        });
        t1.start();
        t2.start();
    }
    

    请注意,synchronized (this) 现在是 synchronized (obj) 和 wait,waitnotify 不是 obj.wait()obj.notify()

    【讨论】:

      【解决方案5】:

      我同意您在错误对象上同步的其他答案。

      我建议“在监视器中思考”。监视器是限制并发访问以保持内部一致状态的对象。因此,监视器清楚地确保了同步,并且不会在任何单个线程中遍布各处。线程不应该相互同步,它们应该通过它们尝试访问的资源来同步。

      public class Main {
      
      
          private static class Monitor {
      
              public synchronized void operation1() {
                  System.out.println(Thread.currentThread().getName() + " is running");
                  try {
                      wait();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println(Thread.currentThread().getName() + " is waken up");
              }
      
      
              public synchronized void operation2() {
                  try {
                      Thread.sleep(3000);
                      System.out.println(Thread.currentThread().getName() + " is running");
                      notify();
                      System.out.println(Thread.currentThread().getName() + " notifying");
                  } catch (InterruptedException e1) {
                      e1.printStackTrace();
                  }
              }
      
          }
      
      
          public static void main(String[] args) {
      
              Monitor monitor = new Monitor();
      
              Thread t1 = new Thread(new Runnable() {
                  @Override
                  public void run() {
                      monitor.operation1();
                  }
              });
      
              Thread t2 = new Thread(new Runnable() {
                  @Override
                  public void run() {
                      monitor.operation2();
                  }
              });
      
              t1.start();
              t2.start();
      
          }
      
      }
      

      【讨论】:

        【解决方案6】:

        除了您锁定不同对象的事实(其他人提到)之外,您的代码中还有另一个问题。您使用Thread.sleep 确保等待线程在其他调用notify 之前开始等待,但是您仍然不能保证它不会以相反的方式发生,从而使等待线程永远等待。

        为了安全起见,您还必须在 while 循环中使用一些“条件变量”。

        阅读oracle's docs 了解示例和进一步说明。

        【讨论】:

        • 是的,最初我使用了一些变量和条件来维持 2 个线程的执行顺序。但要使问题更简单、更短。我使用了 Thread.sleep。感谢分享知识。
        猜你喜欢
        • 1970-01-01
        • 2020-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-20
        • 1970-01-01
        相关资源
        最近更新 更多