首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >让线程暂停- Thread.wait()/Thread.notify()

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

Stack Overflow用户
提问于 2012-01-24 07:44:41
回答 3查看 9K关注 0票数 4

我试着理解线程是如何工作的,我写了一个简单的例子,我想创建并启动一个新的线程,在主线程中显示从1到1000的数字,恢复辅助线程,在辅助线程中显示从1到1000的数字。当我省略Thread.wait()/Thread.notify()时,它的行为与预期一样,两个线程一次都显示几个数字。当我在中添加这些函数时,由于某种原因,主线程号被打印到第二个而不是第一个。我做错了什么?

代码语言:javascript
复制
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();
        }
    }
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-01-24 07:53:43

您误解了wait/notify的工作原理。wait 不会阻塞调用它的线程;它会阻塞当前线程,直到在同一对象上调用notify为止(因此,如果您有线程A和B,并且在线程A中调用了B.wait(),这将停止线程A和而不是线程B-只要B.notify()没有被调用)。

因此,在您的特定示例中,如果您希望主线程首先执行,则需要将wait()放入辅助线程中。如下所示:

代码语言:javascript
复制
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()部分的情况下,辅助线程有机会到达()部分(在您的情况下不太可能,但仍然有可能-如果您将Thread.sleep放在辅助线程的开头,则可以观察到这一点),辅助线程将永远不会被唤醒。因此,最安全的方法如下所示:

代码语言:javascript
复制
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();
        }
    }
}

在本例中,输出是完全确定的。下面是发生的事情:

  1. 主线程创建新的t对象。
  2. 主线程在t thread.
  3. (these线程启动时获得锁定
    1. 辅助线程启动,但由于主线程仍然拥有<t>D28>监视器,辅助线程无法继续,必须等待(因为它的第一条语句是D29,E130不是E231,因为它恰好是E132是E233对象-所有锁,通知和等待也可以在与两个线程中的任何线程完全无关的对象上进行,使用相同的result.
    2. Primary线程继续,到达t.wait()部件并暂停其执行,释放它同步了on.

t监视器

  1. 辅助线程获得t t线程调用t.notify()的所有权,从而唤醒主线程。然而,主线程还不能继续,因为辅助线程仍然持有monitor.
  2. Secondary线程调用monitor.
  3. Primary的所有权,挂起它的执行并释放monitor.
  4. Primary线程最后可以继续,因为t监视器现在是monitor.
  5. Primary线程available.
  6. Primary线程获得t监视器的所有权,但是释放它的权利away.
  7. Primary线程做它的数量计数thing.
  8. Primary线程再次获得调用t.notify()t线程的所有权,唤醒辅助线程。辅助线程还不能继续,因为主线程仍然持有monitor.
  9. Primary线程释放t监视器,away.
  10. Secondary线程获得了t监视器的所有权,但是释放了它,terminates.
  11. The线程做了它的计数工作,然后终止了整个应用程序。t

正如您所看到的,即使在这样一个看似简单的场景中,也会发生很多事情。

票数 11
EN

Stack Overflow用户

发布于 2012-01-24 07:54:30

ExampleThread不是wait()notify(),也不是synchronized。因此,它将在任何时候运行,而不需要与其他线程进行任何协调。

主线程正在等待一个从未到来的通知(这个通知应该由另一个线程发送)。我的猜测是,当ExampleThread终止时,主线程被“错误地”唤醒并完成。

应该等待另一个线程完成的线程必须在检查条件的循环中执行对wait()的调用:

代码语言:javascript
复制
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. */
    ...

然后在你的主线程中:

代码语言:javascript
复制
ExampleThread t = new ExampleThread();
t.start();
/* Do your work. */
...
/* Then signal the other thread. */
t.ready();
票数 2
EN

Stack Overflow用户

发布于 2012-01-24 07:54:38

你很幸运,你的程序完全终止了。

当您调用t.wait()时,您的主线程将停止并无限期地等待通知。

它永远不会得到它,但我相信当辅助线程完成时,它会被伪唤醒唤醒。(请阅读here关于什么是虚假唤醒的内容)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8980240

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档