【问题标题】:End Java threads after a while statement has been run在 while 语句运行后结束 Java 线程
【发布时间】:2015-01-23 13:18:47
【问题描述】:

我的程序完成后,我遇到了结束线程的问题。我运行一个线程时钟对象,它工作得很好,但是当时间'=='一个小时似乎工作时我需要结束所有线程我只需要知道如何结束它们。这是我拥有的代码示例,除了在此代码上方定义的一个 int 之外,这是在 run 方法中运行的唯一内容。

    @Override
    public void run()
    {
        int mins = 5;
        while(clock.getHour() != 1)
        {
            EnterCarPark();
            if(clock.getMin() >= mins)
            {
                System.out.println("Time: " + clock.getTime() + " " + entryPoint.getRoadName() + ": " + spaces.availablePermits() + " Spaces");
                mins += 5;
            }
        }
    }

但是,当您继续观察在 netbeans 的调试模式下运行的线程时,它们会在一个小时后继续运行,不知道如何解决这个问题。我已经尝试过中断调用,但它似乎什么也没做。

【问题讨论】:

  • 抛出 InterruptedException 并捕获它:) 在多线程程序中调试将不正确
  • 你确定循环结束了吗?
  • 是的,我确定循环结束
  • @SME gc 从不收集正在运行的线程。
  • @SME 与所有其他对象相同,但这不会导致线程继续运行。

标签: java multithreading


【解决方案1】:

有两种方法可以很好地停止线程,一种是邪恶的方式。

您只需要访问线程的对象(或者在第一种情况下是在该线程上执行的 Runnable 类)。

因此,您的首要任务是确保您可以访问要停止的所有线程的列表。另请注意,在处理多个线程使用的对象时,您需要确保使用线程安全通信!

现在您有以下选择

中断机制

在每个线程上调用Thread.interrupt()。如果您处于阻塞函数中,这将在线程上抛出 InterruptedException。否则它只会设置isInterrupted() 标志,所以你也必须检查这个。这是一种非常干净且通用的方式,它将尝试通过该线程中断阻塞功能。但是很多人不明白如何将nicely react 转为InterruptedException,所以它可能更容易出现错误。

isRunning 标志

在您的线程中有一个布尔值“isRunning”。 while 循环调用一个函数“stopRunning()”,将该布尔值设置为 false。在您的线程中,您会定期读取此布尔值并在它设置为 false 时停止执行。 这个布尔值需要是线程安全的,这可以通过将其设为volatile(或使用synchronized 锁定)来完成。

当您有 Runnable 时,这也很有效,这是目前在线程上运行任务的建议方式(因为您可以轻松地将 Runnables 移动到 Threadpools 等。

停止线程(EVIL)

第三种邪恶且已弃用的方法是致电Thread.stop()。这是非常不安全的,可能会导致意外的行为,不要这样做!

【讨论】:

  • 另外 Thread.stop() 也因为这个原因而被弃用。见here
  • 好的,我在哪里运行线程中断我已经用连接完成了,但同样的事情发生了我想我可能需要弄清楚如何抛出,因为我不断收到“InterruptedException”需要的错误一个进口,但我有进口。
  • “这将在线程上抛出一个 InterruptedException” - 它不会。它只在少数特定情况下起作用(主要是阻止 IO 调用),否则您必须自己检查标志。对于版本 2,标志必须是易变的。关于第 3 版:感谢线程停止 Knuth 在 Java 7 或 8 iirc 中不再工作。
  • 感谢您的 cmets,我已更新答案以反映这一点。
  • 事实上,当您仍然持有Thread 对象时,isRunning 标志大多是多余的,因为interrupt() 拥有same functionality,还有一些。另请注意,许多阻塞操作是不可中断的,例如网络(套接字)IO,因此必须使用其他方法来处理这些操作,例如从另一个线程关闭套接字。
【解决方案2】:

确保每个线程内的循环都完成 - 如果它在所有线程中都完成,那么输出中有打印是没有意义的。请注意,您在每个循环条件中检查的内容是检查当前时间是否不是下午 1 点,而不是一个小时尚未过去。

此外,您的线程被垃圾收集,这意味着垃圾收集器负责在终止后销毁它们 - 但在这种情况下它们不应该输出任何东西。

【讨论】:

  • 循环结束我在发帖前已经检查过了
  • 你是怎么检查的?抱歉坚持,我只是好奇。谢谢。
  • 如果我在 run 方法中设置一个断点,然后一直按 F5,我可以看到它循环,然后当它到达停止执行的小时。
  • 您确定这些线程仍然处于活动状态,而且还没有被垃圾回收吗?
【解决方案3】:

所有线程共享的volatile变量应该有助于实现目标。 volatile 变量的重要性在于,每个线程都不会缓存或具有本地副本,而是需要直接从主内存中读取。更新后,线程将获取新数据。

    public class A{
     public static volatile boolean letThreadsRun = true;
    }

    // inside your Thread class
    @Override
    public void run()
    {    // there will come a point when A.letThreadsRun will be set to false when desired
         while(A.letThreadsRun)
        {

        }
    }

如果两个线程同时读写共享变量,那么 使用 volatile 关键字是不够的。你需要使用 同步在这种情况下,以保证读写 变量是原子的。

这里有一些链接可以帮助你理解这个概念:

http://tutorials.jenkov.com/java-concurrency/volatile.html

http://java.dzone.com/articles/java-volatile-keyword-0

【讨论】:

    【解决方案4】:

    如果这些线程在您的主程序完成后仍在运行,那么将它们设置为守护线程可能是合适的。 JVM 将在所有非守护线程完成后退出,杀死所有剩余的守护线程。

    如果你像这样启动线程:

    Thread myThread = new MyThread();
    myThread.start();
    

    然后对它们进行守护程序化很简单:

    Thread myThread = new MyThread();
    myThread.setDaemon(true);
    myThread.start();
    

    【讨论】:

      【解决方案5】:

      外部终止线程或依赖外部机制(如 kill)来正确终止程序是一种不好的做法。线程应始终设计为自行终止,并且不会使资源(和共享对象)处于潜在的不确定状态。每次我遇到一个没有按预期停止的线程时,它总是一个编程错误。去检查你的代码,然后在调试器中单步执行运行循环。

      关于你的线程,它应该在小时数达到 1 时自行终止,但如果它低于或高于 1,它不会终止。如果分钟数超过 59,我会确保时钟的小时数达到 1,并检查它是否不会以某种方式跳过 1 并在日落时增加,跳过了唯一的测试值。还要检查 clock.getHour() 实际上是返回小时计数而不是虚拟值或严重不正确的东西。

      【讨论】:

        【解决方案6】:

        您是否考虑过使用 ExecutorService ?它的行为更具可预测性,并避免了线程创建的开销。我的建议是您将 while 循环包装在一个内并设置 1 小时的时间限制。

        【讨论】:

        • @Brandon ExecutorService 的行为更具可预测性,并避免了线程创建的开销。我的建议是简单地将他的 while 循环包裹在一个设定时间限制为 1 小时的循环中。
        • 对,我的意思是你的回答并没有解释这些。
        【解决方案7】:

        使用 Thread.interrupt() 不会停止线程运行,它只是向您的线程发送一个信号。我们的工作是倾听这个信号并采取相应的行动。

        Thread t = new Thread(new Runnable(){
                      public void run(){
                         // look for the signal
                         if(!Thread.interrupted()){
                             // keep doing whatever you're doing
                         }
        
                      }
                 });
        
         // After 1 hour
         t.interrupt();
        

        但与其做所有这些工作,不如考虑使用 ExecutorService。您可以使用带有静态方法的 Executors 类来返回不同的线程池。

        Executors.newFixedThreadPool(10)

        • 创建一个大小为 10 的固定线程池,任何更多作业将进入队列等待稍后处理

        Executors.newCachedThreadPool()

        • 从 0 个线程开始,如果所有现有线程都忙于某项任务,则创建新线程并根据需要将它们添加到池中。这个有一个终止策略,如果一个线程空闲 60 秒,它将从池中删除该线程

        Executors.newSingleThreadExecutor()

        • 创建一个线程,该线程将从队列中提供数据,所有提交的任务将一个接一个地处理。

        您可以将相同的 Runnable 任务提交到您的线程池。 Executors 也有一些方法来获取你可以提交计划任务的池,你希望将来发生的事情

        ExecutorService service = Executors.newFixedThreadPool(10);
        service.execute(myRunnableTask);
        

        关于您的问题,当您使用线程池时,您可以选择在这样的一段时间后关闭它们

        service.shutdown();
        service.awaitTermination(60, TimeUnit.MINUTES);
        

        注意事项

        shutdown() 启动有序关闭,执行之前提交的任务,但不会接受新任务。如果已经关闭,则调用没有额外的效果。

        awaitTermination() 正在等待执行器的状态变为 TERMINATED。但首先,如果调用了 shutdown(),则状态必须进入 SHUTDOWN;如果调用了 shutdownNow(),则状态必须进入 STOP。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-02-26
          • 1970-01-01
          • 2011-10-22
          • 1970-01-01
          • 1970-01-01
          • 2018-06-05
          • 2016-07-15
          • 2016-01-10
          相关资源
          最近更新 更多