让线程暂停 - Thread.wait()/ Thread.notify() [英] Getting a thread to pause - Thread.wait()/Thread.notify()

查看:132
本文介绍了让线程暂停 - Thread.wait()/ Thread.notify()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试理解线程是如何工作的,我写了一个简单的例子,我想创建并启动一个新线程,线程,在主线程中显示1到1000的数字,恢复辅助线程,并在辅助线程中显示1到1000之间的数字。当我省略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. When I leave out the Thread.wait()/Thread.notify() it behaves as expected, both threads display a few numbers at a time. 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();
        }
    }
}


推荐答案

您误解了等待 / 通知的工作原理。 等待 阻止调用它的线程;它会阻止当前线程,直到在同一个对象上调用notify (所以如果你有线程A和B,而在线程A中调用了B.wait(),这将停止线程A和线程B - 只要没有调用B.notify())。

You misunderstand how wait/notify works. wait does not block the thread on which it is called; 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).

所以,在你的具体示例,如果您希望首先执行主线程,则需要将wait()放在辅助线程中。像这样:

So, in your specific example, if you want main thread to execute first, you need to put wait() inside the secondary thread. 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();
        }
    }
}

但是,即使这段代码也可能不像你想要的那样工作。在主线程到前的notify()部分的情况下,辅助线程有机会到达wait()部分(在你的情况下不太可能,但仍然可能 - 你可以观察它)如果你把Thread.sleep放在辅助线程的开头),那么辅助线程永远不会被唤醒。因此,最安全的方法类似于:

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. 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. 主线程创建一个新的 t 对象。

  2. 主线程在 t 监视器上获得锁定。

  3. 主线程启动 t thread。

  4. (这些可以按任何顺序发生)
  1. Main thread creates a new t object.
  2. Main thread gets a lock on the t monitor.
  3. Main thread starts the t thread.
  4. (these can happen in any order)

  1. 辅助线程启动,但是由于主线程仍然拥有 t 监视器,因此辅助线程无法继续并且必须等待(因为它的第一个语句是 synchronized(this)因为 t 对象 - 所有锁定,通知和等待都可以很好地完成一个与2个线程完全无关的对象,结果相同。

  2. 主线程继续,转到 t.wait() part并暂停执行,释放它同步的 t 监视器。

  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.
  2. Primary thread continues, gets to the t.wait() part and suspends its execution, releasing the t monitor that it synchronized on.


  • 辅助线程获得 t 监视器的所有权。

  • Sec ondary线程调用 t.notify(),唤醒主线程。但是主线程还不能继续,因为辅助线程仍然拥有 t 监视器的所有权。

  • 辅助线程调用 t.wait(),暂停执行并释放 t 监视器。

  • 主线程最终可以继续,因为 t 监视器现在可用。

  • 主线程获得的所有权t 监视器,但立即释放它。

  • 主线程进行数字计数。

  • 主线程再次获得所有权 t 监视器。

  • 主线程调用 t.notify() ,唤醒次要线程。辅助线程还不能继续,因为主线程仍然保持 t 监视器。

  • 主线程释放 t 监视并终止。

  • 辅助线程获得 t 监视器的所有权,但是将其释放

  • 辅助线程进行数字计数,然后终止。

  • 整个应用程序终止。

  • Secondary thread gains ownership of t monitor.
  • Secondary thread calls t.notify(), waking the main thread. The main thread cannot continue just yet though, since the secondary thread still holds ownership of the t monitor.
  • Secondary thread calls t.wait(), suspends its execution and releases the t monitor.
  • Primary thread can finally continue, since the t monitor is now available.
  • Primary thread gains ownership of the t monitor but releases it right away.
  • Primary thread does its number counting thing.
  • Primary thread again gains ownership of the t monitor.
  • Primary thread calls t.notify(), waking the secondary thread. The secondary thread cannot continue just yet, because the primary thread still holds the t monitor.
  • Primary thread releases the t monitor and terminates.
  • Secondary thread gains ownership of the t monitor, but releases it right away.
  • Secondary thread does its number counting thing and then terminates.
  • The entire application terminates.
  • 正如你所看到的,即使是在这样一个看似简单的场景中,也会有很多事情发生。

    As you can see, even in such a deceptively simple scenario there is a lot going on.

    这篇关于让线程暂停 - Thread.wait()/ Thread.notify()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆