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



所以我有一个经典的例子,"我的代码可以工作,但我不知道为什么"。

我有一个创建线程的程序,当我从扫描仪接收到某个输入时,我会将字符串的控制权传递给工作线程。为此,我让我的线程等待(),当我从UI线程获得正确的输入时,我通知()。

这是我的密码。为了简单起见,我只使用了一个线程。

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。然而,我真的不明白我在哪个对象上调用notifywait的意义

在我的实现中,我随意地执行t.wait()/t.notify(),其中t是我的线程对象。我想如果我做threadName.wait()/threadName.notify(),它仍然有效。为什么我们要调用notify并等待看似任意的对象?我知道我遗漏了一个关于通知和等待的概念。

事实上,当您在Thread实例上调用wait时,您违反了约定:

建议应用程序不要在Thread实例上使用waitnotifynotifyAll

这是因为Thread将其用于自己的内部目的。

要回答您的问题:Thread对象不是线程。如果说t.notify()不通知t,它会通知在t的监视器上等待的线程。在这种情况下,Thread的实例只是另一个Java对象,所有Java对象都有一个与其关联的监视器。

您建议使用String threadName的监视器是另一个坏主意,因为您不能控制字符串实例的生命周期,并且可能很容易破坏已插入字符串的问题。

建议在线程协调中不要涉及任意对象的监视器,而是更喜欢使用Object的专用实例。这是由关注点分离原则的一般优势推动的。

为什么我们要调用notify并等待看似任意的对象?

调用wait和notify对对象本身没有直接影响,因此它确实是任意的。您所需要的只是一些对象,可以用作通信点。

事实上,Java语言中有人认为,在任意对象上使用等待和通知是可能的,这是一个历史错误,因为管理锁会给所有对象增加很小的成本和复杂性,即使对于绝大多数对象来说,该机制是未使用的。相反,它们应该使用一个专用的Lock类,或者让对象实现一个特定的接口来指示您可以对它们调用wait/notify,或者类似的东西。

无论如何,而不是使用任意对象是个好主意,因为(a)这很令人困惑,(b)它有可能与其他试图使用同一对象的代码纠缠在一起。如果没有合理的对象可用,您应该为此创建一个对象:

    private final Object lock = new Object();

最新更新