14

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

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

4

13 回答 13

12

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

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

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

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

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

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

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

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

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

于 2008-12-23T22:37:59.640 回答
8

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

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

于 2008-12-23T22:30:32.013 回答
6

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

如果你想更优雅地处理“杀死”进程,你可以安装一个关闭钩子,当你点击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;
        }
    }
}

或类似的东西。

于 2008-12-24T04:14:18.013 回答
3

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

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

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

于 2008-12-23T22:57:30.537 回答
3

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

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

这必须有数百种变体。

于 2008-12-23T23:12:00.743 回答
2

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

于 2008-12-23T22:30:55.460 回答
1

关于“程序可能处于 JDBC 调用中间。我怎样才能更优雅地终止它?”的问题。- 请参阅如何中止正在运行的 jdbc 事务?

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

于 2008-12-23T22:59:23.547 回答
0

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

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

不需要那么努力地工作。

于 2008-12-23T22:40:32.630 回答
0

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

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()在希望程序停止时简单地调用该方法,并且它将干净地退出。

于 2008-12-23T23:47:38.690 回答
0

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

但这仍然容易失败。

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

于 2008-12-24T05:00:46.763 回答
0

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

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

于 2009-04-09T11:19:13.373 回答
0

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

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

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

如果您对每秒轮询一次感到满意,那就是 (108 * 1000)。减少 108 000 倍的压力。这也意味着您可以使用与您的一个 while 循环相同的 cpu 应变检查 108 000 个值 - 因为您不会经常分配您的 cpu 进行检查。记住 cpu 有一个时钟周期。我的是 3 600 000 000 赫兹(每秒周期)。

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

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

于 2017-08-11T17:09:33.797 回答
0

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

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

有关详细信息,请参阅JEP 285 。

于 2017-08-11T19:13:27.100 回答