【问题标题】:How to end a thread after coming out of a for loop退出for循环后如何结束线程
【发布时间】:2013-12-06 09:52:17
【问题描述】:

所以我有一个使用 2 个线程打印 Hello 和 Goodbye 的程序。 我需要打印 Hello 100 次和 Goodbye 100 次。 这工作正常,但是,在打印之后,程序挂起并且没有完成。 我有一种感觉是因为线程没有完成。

我想知道最好的方法是什么?

所以我的主要是这样的

public static void main(String [] args)
{
    Object lock = new Object();

    new HelloThread(lock).start();
    new GoodbyeThread(lock).start();
}

这里是 HelloThread 类。 GoodbyeThread 看起来一样:

    class HelloThread extends Thread

    {
        private Object lock;
        private boolean isRunning = false;

    HelloThread(Object l)
    {
        this.lock = l;
    }

    public void run()
    {
        for(int i = 0; i < 100; i++)
        {
            synchronized(lock)
            {
                isRunning = true;
                System.out.println("Hello");
                lock.notify();

                try
                {
                    lock.wait();
                }
                catch(InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }
}

我在想可能有一种方法可以使用 isRunning 变量来关闭线程,但是我不确定最好的方法是什么。 感谢您的帮助

【问题讨论】:

  • isRunning 变量的意义何在?
  • @MadConan 这只是我为了让它工作而留下的东西

标签: java multithreading synchronized


【解决方案1】:

for 循环并不重要,关键的事情发生在最后一次迭代中。很可能其中一个线程在另一个线程已经执行了它的notify() 之后会进入它的wait(),从而错过了突破wait() 的唯一机会。

【讨论】:

  • 嗯,有道理。你怎么能解决这个问题?
  • 首先完全不使用wait-notify 机制。它是低级的,容易出现很多问题;推荐的方法是使用java.util.concurrent 包中的同步工具之一。正如我认为你想要两个线程的严格交错:一个执行一个步骤,然后另一个。您可以使用两个 CountDownLatches 或单个 Phaser 来实现。
  • 啊,我明白了,所以我不应该真的使用等待通知。我有一种感觉我做错了,因为我记得在大学的线程模块中,使用等待和通知太简单了哈哈
【解决方案2】:

你的 GoodbyeThread 正在等待你的 HelloThread 发送一个 notify()。

i == 99 时可能发生的情况:

HelloThread 打印“Hello”调用 notify(),然后调用 wait()

GoodbyeThread 打印“再见”调用 notify() [这里 HelloThread 退出 for 循环并终止],然后调用 wait()。

什么都不会调用 notify() 来结束 GoodbyeThread 的 wait()。

一种解决方法是仅在 i

【讨论】:

  • 啊,我明白了!对我来说可能有更好的方法是吗?
【解决方案3】:

这工作正常,但是在打印后,程序挂起并且没有完成。我有一种感觉是因为线程没有完成。

如果线程离开run() 方法,那么它将结束。我怀疑你的线程在"Hello" 的第 100 次打印后卡在了lock.wait() 上。如果您使用 jconsole 或dump a thread stack trace,您应该能够看到线程卡在哪里。

需要意识到的是,如果您调用lock.notify(),其他线程唯一会收到通知的时间是它是否在lock.wait() 中。通知呼叫不排队。

【讨论】:

  • 是的,这听起来很对。只需要弄清楚如何解决这个问题。这个名字是什么?这是僵局吗?
  • 这不是死锁@AndyOHart。这只是糟糕的逻辑。
  • 我添加了更多详细信息@AndyOHart,如果其他线程没有等待,则不会保存通知。
【解决方案4】:

如上所述:线程正在死锁,因为其中至少有一个仍在等待 notify() 调用。

假设你只有一次跑步要做(只打印一次“Hello world”):

  1. 线程 A 启动,打印“Hello”,然后运行 ​​notify(),但没有人在监听,然后 wait()。
  2. 线程 B 启动,打印“World”,然后运行 ​​notify()。
  3. 线程 A 唤醒并尝试重新获得监视器 lock,但这个监视器仍由线程 B 持有,因此线程 A 必须进一步等待。
  4. 线程 B 运行 wait(),释放监视器。
  5. 线程 A 再次唤醒,重新获得监视器并完成循环。

线程 B 仍在等待,但从未被唤醒。

这可以通过这样的退出通知来解决:

for(int i = 0; i < 100; i++) {
  // ...
}
synchronized (lock) {
  lock.notifyAll();
}

wait/notify 结构是最基本的线程工具,并不适合解决许多线程问题,因为它们经常会导致死锁或更糟。对于更简单且不易出错的线程概念,您应该使用concurrent package,它有现成的工具可用于解决大多数问题。

但是熟悉这些工具是很有必要的,而且你绝对必须围绕这些工具来设计你的代码,而不是反过来。一个现有的程序不能仅仅用多线程来填充以使其更快,它需要在你编写第一行代码之前为其设计。否则你会遇到很多问题,包括你的结果可能比单线程执行要慢。

【讨论】:

    猜你喜欢
    • 2022-01-18
    • 1970-01-01
    • 2019-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-22
    相关资源
    最近更新 更多