如果我只是想在线程之间发送信号,为什么使用wait()/notify()的对象很重要呢?

4

我有一个典型的问题:“我的代码可以运行,但我不知道为什么”。

我有一个程序创建了一个线程,当我从扫描器接收到特定输入时,我将字符串的控制权传递给工作线程。为此,我使我的线程wait(),当我从UI线程获得正确的输入时,我会notify()。

以下是我的代码。为简单起见,我只使用了一个线程。

package main;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;


class ThreadDemo extends Thread {
       private Thread t;
       private String threadName;
       volatile Boolean keepRunning = true;
       private Queue<String> q = new LinkedList<String>();


       ThreadDemo( String name){
           threadName = name;
           System.out.println("Creating " +  threadName );
       }

       public void in(String ex){
           q.add(ex);
           System.out.println("Added " + ex + "to queue of " + threadName);
           synchronized(t){
               t.notify();
           }       
       }


       public void run() {
           System.out.println("Starting to loop.");
            while (keepRunning) {
                try {
                    //Why does it matter that I synchronized t? 
                    synchronized(t){
                        System.out.println(threadName +  "Waiting");
                        t.wait();
                    }
                } catch (InterruptedException e) {
                    System.out.println("Thread interrupted " + e.toString());
                    }
                System.out.println(threadName +  "notified");
                if (q.size()>0){
                    String out = q.remove();
                    System.out.println(threadName + "received " + out);
                }
            }
            System.out.println("Done looping.");
       }

       public void start ()
       {
          System.out.println("Starting " +  threadName );
          if (t == null)
          {
             t = new Thread (this, threadName);
             t.start ();
          }
       }
    }


public class DataAnalysisProgram {

    public static void main(String[] args) {
          ThreadDemo T1 = new ThreadDemo( "Thread-1");
          T1.start();

          System.out.println("say something");
          Scanner s = new Scanner(System.in);
          String t;
          do{
              t = s.next();
              T1.in(t);
          }while (!t.equals("stop"));

          T1.keepRunning = false;
          T1.interrupt();
          s.close();
    }
} 

这个代码可以正常工作。我的线程会等待直到我使用notify。然而,我并不真正理解我调用notify和wait的对象的重要性。

在我的实现中,我随意使用了t.wait()/t.notify(),其中t是我的线程对象。如果我使用threadName.wait()/threadName.notify(),它也应该能工作。为什么我们要在看似随意的对象上调用notify和wait呢?我知道我在notify和wait方面缺少一些概念。

2个回答

6
事实上,当您在 Thread 实例上调用 wait 时,您违反了合同:

建议应用程序不要在 Thread 实例上使用 wait notify notifyAll

这是因为 Thread 将其用于自己的内部目的。
回答您的问题: Thread 对象不是线程。说 t.notify() 不会通知 t ,它会通知等待在 t 的监视器上的线程。在此上下文中, Thread 的实例只是另一个Java对象,所有Java对象都有与之关联的监视器。
您建议使用 String threadName 的监视器也是一个坏主意,因为您无法控制字符串实例的生命周期,并且可能很容易地破坏使用已强制转换的字符串的问题。
推荐的做法是不涉及任意对象的监视器来进行线程协调,而是优先使用 Object 的专用实例。这是由关注点分离原则的一般优势所驱动的。

2
为什么要在看似任意的对象上调用notify和wait方法?
调用wait和notify没有直接影响对象本身,所以它确实是任意的。你只需要一些可以用作通信点的对象即可。
事实上,有人认为在Java语言中使用任意对象进行wait和notify操作是历史性错误,因为对于绝大多数对象,管理锁定机制都会增加一些小成本和复杂性,而这种机制往往是没有用到的。相反,他们应该使用一个专用的Lock类或使对象实现特定接口来指示您可以在其上调用wait/notify等操作,或者类似的方案。
无论如何,最好不要使用任意对象,因为(a)它容易造成混淆,(b)它可能会与其他试图使用相同对象的代码纠缠在一起。如果没有合适的对象可用,应为此创建一个对象。
    private final Object lock = new Object();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接