【问题标题】:Java while loop and Threads! [duplicate]Java while 循环和线程! [复制]
【发布时间】:2010-09-28 06:17:22
【问题描述】:

我有一个程序可以不断地轮询数据库中某个字段的值的变化。它在后台运行,当前使用 while(true) 和 sleep() 方法来设置间隔。我想知道这是否是一个好习惯?而且,有什么更有效的方法来实现这一点?该程序旨在始终运行。

因此,停止程序的唯一方法是在进程 ID 上发出终止命令。该程序可能处于 JDBC 调用的中间。我怎样才能更优雅地终止它?我知道最好的选择是通过使用将由线程定期检查的标志来设计某种退出策略。但是,我想不出改变这个标志值的方法/条件。有什么想法吗?

【问题讨论】:

    标签: java multithreading while-loop


    【解决方案1】:

    我认为你应该使用 timertask 来轮询它。

    我的计算机在 10 秒内运行了 1075566 次 while 循环。 那是一秒钟内 107557 次。

    真正需要多久进行一次轮询? TimerTask 在 1 秒内以最快的速度运行 1000 次。你给它一个 int (毫秒)的参数作为参数。如果您对此感到满意 - 这意味着您的 CPU 压力将减少 108 倍。

    如果您愿意每秒轮询一次 (108 * 1000)。减少 108 000 倍的压力。这也意味着您可以检查 108 000 个值,其 cpu 应变与您在一个 while 循环中所具有的相同 - 因为您没有分配您的 cpu 来经常检查。记住 cpu 有一个时钟周期。我的是 3 600 000 000 赫兹(每秒周期)。

    如果您的目标是为用户更新它 - 您可以在用户每次登录时运行检查(或手动让他要求更新) - 这实际上不会对 cpu 造成任何压力。

    您还可以使用thread.sleep(miliseconds); 来降低您的轮询线程的压力(因为它不会经常轮询)。

    【讨论】:

      【解决方案2】:

      Java9 对此有另一个“潜在”答案:Thread.onSpinWait():

      表示调用者暂时无法继续,直到其他活动发生一个或多个操作。通过在自旋等待循环构造的每次迭代中调用此方法,调用线程向运行时指示它正在忙于等待。运行时可能会采取措施来提高调用自旋等待循环构造的性能。

      更多详情请见JEP 285

      【讨论】:

        【解决方案3】:

        正如其他人所说,您必须进行轮询这一事实可能表明您的系统设计存在更深层次的问题......但有时就是这样,所以......

        如果你想更优雅地处理“杀死”进程,你可以安装一个关闭钩子,当你点击 Ctrl+C 时调用它:

        volatile boolean stop = false;
        
        Runtime.getRuntime().addShutdownHook(new Thread("shutdown thread") {
        
            public void run() {
        
                stop = true;
            }
        });
        

        然后定期检查停止变量。

        更优雅的解决方案是等待事件:

        boolean stop = false;
        
        final Object event = new Object();
        
        Runtime.getRuntime().addShutdownHook(new Thread("shutdown thread") {
        
            public void run() {
        
                synchronized(event) {
        
                    stop = true;
                    event.notifyAll();
                }
            }
        });
        
        // ... and in your polling loop ...
        synchronized(event) {
        
            while(!stop) {
        
                // ... do JDBC access ...
                try {
        
                    // Wait 30 seconds, but break out as soon as the event is fired.
                    event.wait(30000);
                }
        
                catch(InterruptedException e) {
        
                    // Log a message and exit. Never ignore interrupted exception.
                    break;
                }
            }
        }
        

        或者类似的东西。

        【讨论】:

          【解决方案4】:

          我想知道这是否是一个好习惯?

          没有。这样不好。有时,这就是你所拥有的一切,但这并不好。

          还有,有什么更有效的方法来实现这一点?

          首先是如何进入数据库的?

          最好的更改是修复插入/更新数据库的程序,以向数据库和您的程序发出请求。 JMS 主题很适合这种事情。

          下一个最佳更改是向数据库添加一个触发器,以将每个插入/更新事件排入队列。该队列可以提供一个 JMS 主题(或队列)以供您的程序处理。

          后备计划是您的轮询循环。

          但是,您的轮询循环不应轻易发挥作用。它应该将消息放入队列中,以供其他一些 JDBC 进程处理。终止请求是可以放入 JMS 队列的另一条消息。当您的程序收到终止消息时,它绝对必须完成之前的 JDBC 请求,并且可以正常停止。

          在执行上述任何操作之前,请先查看 ESB 解决方案。 Sun 的JCAPSTIBCO 已经有了这个。像 MulesourceJitterbit 这样的开源 ESB 可能已经构建并测试了此功能。

          【讨论】:

          • +1 以获得全面的答案!
          【解决方案5】:

          我很惊讶没有人提到用 Java 实现的中断机制。它应该是解决停止线程问题的方法。所有其他的解决方案都至少有一个缺陷,这就是为什么需要在 Java 并发库中实现这种机制的原因。

          你可以通过发送一个 interrupt() 消息来停止一个线程,但是还有其他方法可以让线程被中断。发生这种情况时会抛出 InterruptedException。这就是为什么在调用 sleep() 时必须处理它的原因。这就是您可以进行清理并优雅结束的地方,例如关闭数据库连接。

          【讨论】:

            【解决方案6】:

            您可以使该字段成为包含(概念上)进程 ID 和时间戳的复合值。 [更好的是,使用两个或更多字段。] 在拥有该字段访问权限的进程中启动一个线程,并让它循环、休眠并更新时间戳。然后等待拥有对该字段的访问权限的轮询进程可以观察到时间戳在某个时间 T(远大于更新循环的睡眠间隔的时间)内没有更新,并假设先前拥有的进程已经死亡.

            但这仍然容易失败。

            在其他语言中,我总是尝试使用flock() 调用来同步文件。不确定Java等价物是什么。尽可能获得真正的并发。

            【讨论】:

              【解决方案7】:

              如果这是您的应用程序并且您可以修改它,您可以:

              • 让它读取文件
              • 读取标志的值。
              • 当你想杀死它时,你只需修改文件,应用程序就会优雅地退出。

              不需要那么辛苦。

              【讨论】:

              • 由于是一个间隔8秒左右的while循环,读取文件所涉及的IO会非常多。
              【解决方案8】:

              我在当前公司的实用程序库中为这些问题创建了一个服务类:

              public class Service implements Runnable {
              
                  private boolean shouldStop = false;
              
                  public synchronized stop() {
                      shouldStop = true;
                      notify();
                  }
              
                  private synchronized shouldStop() {
                      return shouldStop;
                  }
              
                  public void run() {
                      setUp();
                      while (!shouldStop()) {
                          doStuff();
                          sleep(60 * 1000);
                      }
                  }
              
                  private synchronized sleep(long delay) {
                      try {
                          wait(delay);
                      } catch (InterruptedException ie1) {
                          /* ignore. */
                      }
                  }
              
              }
              

              当然,这还远未完成,但您应该了解要点。当您希望程序停止时,这将使您能够简单地调用stop() 方法,它会干净地退出。

              【讨论】:

              【解决方案9】:

              这是Java。将您的处理移至第二个线程。现在你可以

              • 在循环中从标准输入读取。如果有人键入“QUIT”,请将 while 标志设置为 false 并退出。
              • 使用 STOP 按钮创建 AWT 或 Swing 框架。
              • 假装你是一个 Unix 守护进程并创建一个服务器套接字。等待有人打开套接字并发送“QUIT”。 (这有一个额外的好处,您可以将睡眠更改为带有超时的选择。)

              这一定有数百种变体。

              【讨论】:

                【解决方案10】:

                关于“程序可能正在执行 JDBC 调用。如何才能更优雅地终止它?”的问题。 - 见How can I abort a running jdbc transaction?

                请注意,使用带有 sleep() 的轮询很少是正确的解决方案 - 实施不当,最终可能会占用 CPU 资源(JVM 线程调度程序最终会花费过多的时间来休眠和唤醒线程)。

                【讨论】:

                  【解决方案11】:

                  请注意,Timer(或类似的)会更好,因为您至少可以重用它并让它处理睡眠、调度、异常处理等所有细节......

                  您的应用可能会死掉的原因有很多。不要只关注一个。

                  如果理论上你的 JDBC 工作有可能让事情处于半正确的状态,那么你就有了一个应该修复的错误。您所有的数据库工作都应该在一个事务中。它应该去还是不去。

                  【讨论】:

                    【解决方案12】:

                    为 SIGTERM 设置一个信号处理程序,该处理程序设置一个标志,告诉您的循环下次退出。

                    【讨论】:

                    • "为 SIGTERM 设置信号处理程序" 你是怎么做到的?
                    • google 'java 信号处理程序'。
                    【解决方案13】:

                    这确实是一个太大的问题,无法以这种格式完全回答。帮自己一个忙,去购买Java Concurrency in Practice。在 Java 5+ 平台上没有更好的并发资源。有整章专门讨论这个主题。

                    关于在 JDBC 调用期间终止进程的主题,应该没问题。我相信中断 JDBC 调用存在问题(因为你不能?)但这是一个不同的问题。

                    【讨论】:

                    • +1 如果你觉得 Goetz 的书有点压倒性(我一开始是这样做的,里面有很多信息)我建议阅读 Java Threads 3rd edition 以获得更简单的介绍和更多代码.
                    • 这个具体问题有总结吗?像“改用这个类”?还是什么?
                    猜你喜欢
                    • 2015-06-14
                    • 1970-01-01
                    • 1970-01-01
                    • 2013-06-15
                    • 2014-05-27
                    • 1970-01-01
                    • 2016-04-11
                    • 2021-02-09
                    • 1970-01-01
                    相关资源
                    最近更新 更多