简体   繁体   English

让线程暂停 - Thread.wait()/ Thread.notify()

[英]Getting a thread to pause - Thread.wait()/Thread.notify()

I'm trying to understand how threads work, and I wrote a simple example where I want to create and start a new thread, the thread, display the numbers from 1 to 1000 in the main thread, resume the secondary thread, and display the numbers from 1 to 1000 in the secondary thread. 我试图理解线程如何工作,我写了一个简单的例子,我想创建并启动一个新线程,线程,在主线程中显示1到1000的数字,恢复辅助线程,并显示次要线程中的数字从1到1000。 When I leave out the Thread.wait()/Thread.notify() it behaves as expected, both threads display a few numbers at a time. 当我省略Thread.wait()/ Thread.notify()时,它的行为与预期一致,两个线程一次显示几个数字。 When I add those functions in, for some reason the main thread's numbers are printed second instead of first. 当我添加这些函数时,由于某种原因,主线程的数字是第二个而不是第一个打印的。 What am I doing wrong? 我究竟做错了什么?

public class Main {

    public class ExampleThread extends Thread {

        public ExampleThread() {
            System.out.println("ExampleThread's name is: " + this.getName());
        }

        @Override
        public void run() {         
            for(int i = 1; i < 1000; i++) {
                System.out.println(Thread.currentThread().getName());
                System.out.println(i);
            }
        }
    }

    public static void main(String[] args) {
        new Main().go();
    }

    public void go() {
        Thread t = new ExampleThread();
        t.start();

        synchronized(t) {
            try {
                t.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        for(int i = 1; i < 1000; i++) {
            System.out.println(Thread.currentThread().getName());
            System.out.println(i);
        }

        synchronized(t) {
            t.notify();
        }
    }
}

You misunderstand how wait / notify works. 你误解了wait / notify工作原理。 wait does not block the thread on which it is called; wait 并未阻挡它被称为线程; it blocks the current thread until notify is called on the same object (so if you have threads A and B and, while in thread A, called B.wait(), this will stop thread A and not thread B - for as long as B.notify() is not called). 它阻塞当前线程,直到在同一个对象上调用notify(因此,如果你有线程A和B,而在线程A中,称为B.wait(),这将停止线程A而不是线程B - 只要B.notify()未被调用)。

So, in your specific example, if you want main thread to execute first, you need to put wait() inside the secondary thread. 因此,在您的具体示例中,如果您希望首先执行主线程,则需要将wait()放在辅助线程中。 Like this: 像这样:

public class Main {

    public class ExampleThread extends Thread {

        public ExampleThread() {
            System.out.println("ExampleThread's name is: " + this.getName());
        }

        @Override
        public void run() {         
            synchronized (this) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
            for(int i = 1; i < 1000; i++) {
                System.out.println(Thread.currentThread().getName());
                System.out.println(i);
            }
        }
    }

    public static void main(String[] args) {
        new Main().go();
    }

    public void go() {
        Thread t = new ExampleThread();
        t.start();

        for(int i = 1; i < 1000; i++) {
            System.out.println(Thread.currentThread().getName());
            System.out.println(i);
        }

        synchronized(t) {
            t.notify();
        }
    }
}

However, even this code may not work like you want. 但是,即使此代码可能无法正常工作。 In a scenario where the main thread gets to the notify() part before the secondary thread had a chance to get to the wait() part (unlikely in your case, but still possible - you can observe it if you put Thread.sleep at the beginning of the secondary thread), the secondary thread will never be waken up. 在主线程有可能到达wait()部分之前到达notify()部分的情况下(在你的情况下不太可能,但仍然可能 - 你可以观察它,如果你把Thread.sleep放在在辅助线程的开头),辅助线程永远不会被唤醒。 Therefore, the safest method would be something similar to this: 因此,最安全的方法类似于:

public class Main {

    public class ExampleThread extends Thread {

        public ExampleThread() {
            System.out.println("ExampleThread's name is: " + this.getName());
        }

        @Override
        public void run() {
            synchronized (this) {
                try {
                    notify();
                    wait();
                } catch (InterruptedException e) {
                }
            }
            for(int i = 1; i < 1000; i++) {
                System.out.println(Thread.currentThread().getName());
                System.out.println(i);
            }
        }
    }

    public static void main(String[] args) {
        new Main().go();
    }

    public void go() {
        Thread t = new ExampleThread();
        synchronized (t) {
            t.start();
            try {
                t.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        for(int i = 1; i < 1000; i++) {
            System.out.println(Thread.currentThread().getName());
            System.out.println(i);
        }

        synchronized(t) {
            t.notify();
        }
    }
}

In this example the output is completely deterministic. 在此示例中,输出完全是确定性的。 Here's what happens: 这是发生的事情:

  1. Main thread creates a new t object. 主线程创建一个新的t对象。
  2. Main thread gets a lock on the t monitor. 主线程在t监视器上获得锁定。
  3. Main thread starts the t thread. 主线程启动t线程。
  4. (these can happen in any order) (这些可以按任何顺序发生)
    1. Secondary thread starts, but since main thread still owns the t monitor, the secondary thread cannot proceed and must wait (because its first statement is synchronized (this) , not because it happens to be the t object - all the locks, notifies and waits could as well be done on an object completely unrelated to any of the 2 threads with the same result. 辅助线程启动,但由于主线程仍然拥有t监视器,辅助线程无法继续并且必须等待(因为它的第一个语句是synchronized (this)而不是因为它碰巧 t对象 - 所有锁,通知和等待也可以在与具有相同结果的2个线程中的任何一个完全无关的对象上完成。
    2. Primary thread continues, gets to the t.wait() part and suspends its execution, releasing the t monitor that it synchronized on. 主线程继续,到达t.wait()部分并暂停其执行,释放它同步的t监视器。
  5. Secondary thread gains ownership of t monitor. 辅助线程获得t monitor的所有权。
  6. Secondary thread calls t.notify() , waking the main thread. 辅助线程调用t.notify() ,唤醒主线程。 The main thread cannot continue just yet though, since the secondary thread still holds ownership of the t monitor. 然而,主线程还不能继续,因为辅助线程仍然拥有t监视器的所有权。
  7. Secondary thread calls t.wait() , suspends its execution and releases the t monitor. 辅助线程调用t.wait() ,暂停其执行并释放t监视器。
  8. Primary thread can finally continue, since the t monitor is now available. 主线程最终可以继续,因为t监视器现在可用。
  9. Primary thread gains ownership of the t monitor but releases it right away. 主线程获得t监视器的所有权,但立即释放它。
  10. Primary thread does its number counting thing. 主线程进行数量计数。
  11. Primary thread again gains ownership of the t monitor. 主线程再次获得t监视器的所有权。
  12. Primary thread calls t.notify() , waking the secondary thread. 主线程调用t.notify() ,唤醒辅助线程。 The secondary thread cannot continue just yet, because the primary thread still holds the t monitor. 辅助线程还不能继续,因为主线程仍然保持t监视器。
  13. Primary thread releases the t monitor and terminates. 主线程释放t监视器并终止。
  14. Secondary thread gains ownership of the t monitor, but releases it right away. 辅助线程获得t监视器的所有权,但立即释放它。
  15. Secondary thread does its number counting thing and then terminates. 辅助线程进行数字计数,然后终止。
  16. The entire application terminates. 整个应用程序终止。

As you can see, even in such a deceptively simple scenario there is a lot going on. 正如您所看到的,即使在这样一个看似简单的场景中,也会发生很多事情。

ExampleThread doesn't wait() or notify() , and isn't synchronized on anything. ExampleThread没有wait()notify() ,并且没有在任何事情上synchronized So it will run whenever it can without any coordination with other threads. 所以只要它可以在没有与其他线程协调的情况下运行它。

The main thread is waiting for a notification which never comes (this notification should be sent by another thread). 主线程正在等待从未发出的通知(此通知应由另一个线程发送)。 My guess is that when the ExampleThread dies, the main thread is woken "spuriously," and completes. 我的猜测是,当ExampleThread死亡时,主线程被“虚假地”唤醒并完成。

The thread that should wait for another to complete must perform the call to wait() inside a loop that checks for a condition: 应该等待另一个完成的线程必须在检查条件的循环内执行wait()调用:

class ExampleThread extends Thread {

  private boolean ready = false;

  synchronized void ready() { 
    ready = true; 
    notifyAll();
  }

  @Override
  public void run() {
    /* Wait to for readiness to be signaled. */
    synchronized (this) {
      while (!ready)
        try {
          wait();
        } catch(InterruptedException ex) {
          ex.printStackTrace();
          return; /* Interruption means abort. */
        }
    }
    /* Now do your work. */
    ...

Then in your main thread: 然后在你的主线程中:

ExampleThread t = new ExampleThread();
t.start();
/* Do your work. */
...
/* Then signal the other thread. */
t.ready();

You are lucky that your program terminates at all. 你很幸运,你的程序终止了。

When you call t.wait() your main threads stops and waits indefinitely on a notification. 当你调用t.wait()你的主线程会停止并无限期地等待通知。

It never gets it, but I believe is awaken by spurious wakeup when the secondary thread finishes. 它永远不会得到它,但我相信当辅助线程结束时, 虚假的唤醒会唤醒它。 (Read here on what a spurious wakeup is). (阅读这里什么一个虚假唤醒是)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM