Java的while循环和线程!

14

我有一个程序,不断地轮询数据库中某个字段的值是否发生变化。它在后台运行,目前使用while(true)和sleep()方法设置时间间隔。我想知道这是否是一种好的做法?还有,是否有更有效的实现方式?该程序需要始终运行。

因此,停止程序的唯一方法是对进程ID执行kill命令。该程序可能正在进行JDBC调用。如何更优雅地终止程序?我知道最好的选择是通过使用一个标志来设计一种退出策略,并定期检查线程。但是,我无法想到一种改变该标志值的方法/条件。有什么建议吗?

13个回答

12

我想知道这是否是一个好的做法?

不是。有时候,这可能是你所拥有的唯一选择,但并不是好的做法。

那么,有什么更有效的方法来实现这个功能呢?

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

最好的改变是修复插入/更新数据库的程序,使请求同时到达数据库和您的程序。使用 JMS 主题处理这种情况非常好。

其次,可以在数据库中添加触发器,将每个插入/更新事件加入队列。该队列可以为您的程序提供处理的 JMS 主题(或队列)。

如果以上方法都无法实现,那么就只能使用轮询了。

但是,您的轮询循环不应该仅仅进行简单的工作。它应该将消息放入队列中,以供其他 JDBC 进程处理。终止请求是另一条可以放入 JMS 队列的消息。当您的程序接收到终止消息时,必须确保已经完成了之前的 JDBC 请求,并且可以优雅地停止。

在执行任何操作之前,请查看 ESB 解决方案。像 Sun 的 JCAPSTIBCO 这样的公司已经拥有这项功能。开源 ESB,如 MulesourceJitterbit 可能已经内置并测试了此功能。


8
这个问题太大了,无法在这种格式下完全回答。请自己去购买Java Concurrency in Practice,这是Java 5+平台上并发性的最佳资源,有整章专门讲解这个主题。
关于在JDBC调用期间终止进程的问题,应该没有问题。我认为中断JDBC调用存在问题(也就是你不能中断?),但那是另一个问题。

1
如果你觉得Goetz的书有点吓人(我一开始也这样认为,里面有很多信息),我建议读Java Threads第三版,以更多的代码作为入门介绍。如果你同意,请点赞+1。 - blank
有没有这个特定问题的摘要?比如“使用这个类代替”之类的东西? - OscarRyz

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;
        }
    }
}

或者类似于此。


3
注意,使用计时器(或类似工具)更好,因为您可以重复使用它,并让它处理所有细节,例如睡眠、调度、异常处理等等...
您的应用程序可能会死机有很多原因,请不要只关注一个。
如果您的JDBC工作在半正确的状态下留下问题,那么您应该修复错误。您所有的数据库工作都应该在一个事务中完成,要么成功要么失败。

3
这是Java。将处理移动到第二个线程中。现在,您可以:
  • 循环从stdin读取。如果有人键入“QUIT”,则将while标志设置为false并退出。
  • 创建一个带有STOP按钮的AWT或Swing框架。
  • 假装自己是Unix守护程序并创建服务器套接字。等待某人打开套接字并发送“QUIT”。(这还有额外的好处,即可以将睡眠更改为具有超时的select。)
肯定有数百种变体。

2

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


如何为 SIGTERM 设置信号处理程序?请设置信号处理器。 - OscarRyz
谷歌“Java信号处理程序”。 - chaos

1
关于问题“程序可能在JDBC调用的中间。我该如何更优雅地终止它?” - 请参见如何中止正在运行的jdbc事务? 请注意,使用带有sleep()的轮询很少是正确的解决方案 - 如果实现不当,它可能会占用CPU资源(JVM线程调度器最终花费过多时间睡眠和唤醒线程)。

0
如果这是你的应用程序并且你可以修改它,你可以:
  • 让它读取一个文件
  • 读取标志位的值。
  • 当你想要终止它时,只需修改文件,应用程序将会优雅地退出。
不需要比这更努力了。

由于这是一个间隔约为8秒的while循环,涉及到读取文件的IO将会相当多。 - Epitaph

0

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

但这仍然容易出现故障。

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


0
我很惊讶没有人提到Java中实现的中断机制。它被认为是停止线程问题的解决方案。所有其他解决方案都至少有一个缺陷,这就是为什么需要在Java并发库中实现此机制的原因。
您可以通过向线程发送interrupt()消息来停止线程,但也有其他方式可以中断线程。当发生这种情况时,将抛出InterruptedException。这就是为什么在调用sleep()等方法时必须处理它的原因。这就是您可以进行清理并优雅地结束,例如关闭数据库连接的地方。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接