Java while循环和线程!

问题描述:

我有一个程序,不断轮询数据库的某些字段的值的变化。它在后台运行,目前使用一段时间(true)和一个sleep()方法来设置时间间隔。我想知道这是否是一种好的做法?而且,有什么可能是更有效的方式来实现呢?该计划旨在随时运行。Java while循环和线程!

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

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

不,它不好。有时候,这只是你所拥有的,但并不好。

而且,有什么可能是更有效的方法来实现呢?

事情到底如何进入数据库?

最好的修改是修复插入/更新数据库的程序,使请求进入数据库和程序。 JMS主题适合这种事情。

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

回退计划是您的投票循环。

但是,您的轮询循环不应该不起作用。它应该将消息放入一个队列中,以供其他JDBC进程使用。终止请求是另一个可以放入JMS队列的消息。当程序获得终止消息时,必须完成之前的JDBC请求并可以正常停止。

在做任何这些之前,请看ESB解决方案。 Sun的JCAPSTIBCO已经有这个。像MulesourceJitterbit这样的开源ESB可能已经具有已经构建和测试过的此功能。

+0

+1全面解答! – 2012-06-06 18:53:45

这对于以这种格式完全回答的问题实在太大了。帮你一个忙,去买Java Concurrency in Practice。在那里的Java 5+平台没有更好的并发资源。有整章致力于这个问题。

关于在JDBC调用期间杀死进程的问题,应该没问题。我相信有中断JDBC调用的问题(因为你不能这么做),但这是一个不同的问题。

+1

+1如果您发现Goetz的书有点压倒性的(我刚开始的时候有很多的信息),我建议您阅读Java Threads 3rd edition,以获得更多的代码。 – blank 2008-12-23 22:40:54

+0

有没有关于这个特定问题的总结?像“使用这个类代替”?或者其他的东西? – OscarRyz 2008-12-23 22:42:20

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

+0

“为SIGTERM设置信号处理程序”您如何做到这一点? – OscarRyz 2008-12-23 22:43:21

+0

谷歌'java信号处理程序'。 – chaos 2008-12-24 06:49:13

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

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

不需要工作,那就更难了。

注意,一个计时器(或类似)将是你至少可以重复使用它,让它与所有的睡眠,调度,异常处理等细节做的更好......

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

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

关于“程序可能在JDBC调用中,我怎么能更好地终止它?” - 请参阅How can I abort a running jdbc transaction?

请注意,使用带sleep()的轮询很少是正确的解决方案 - 执行不当,最终可能导致占用CPU资源(JVM线程调度程序最终花费过多的时间睡眠并唤醒线)。

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

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

必须有数百个变种对此。

我创建了一个服务类在我目前的公司的公用库这类问题:

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()方法,并且它将完全退出。

正如其他人所说,事实上,你必须进行民意调查可能表明你的系统设计有更深层次的问题......但有时候这是它的方式,所以......

如果你想处理“杀”的过程中多了几分优雅,你可以安装一个关闭挂钩,当你打按Ctrl +ç其中被称为:

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

或者类似的东西。

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

但这仍然容易失败。

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

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

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

我想你应该用timertask来轮询它。

我的电脑在10秒内运行一个while循环1075566次。 这就是一秒钟的107557次。

真的需要多久进行一次轮询? TimerTask在1秒内以最快的速度运行1000次。你以int(毫秒)为参数给它一个参数。如果你对此满意 - 这意味着你在这个任务中压缩你的cpu少了108倍。

如果您很满意每秒轮询一次(108 * 1000)。减少108 000次。这也意味着你可以检查108 000个值,与你使用同一个循环的cpu应变相同 - 因为你不会像过去那样指定你的cpu来检查。记住CPU有一个时钟周期。矿井为36亿000赫兹(每秒周期)。

如果您的目标是让用户更新 - 您可以在用户每次登录时运行一次检查(或手动让他请求更新) - 这实际上不会给CPU带来任何负担。

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

Java9还有一个“势”的回答是:Thread.onSpinWait()

表示呼叫者暂时无法取得进展,直到对其他活动的部分一个或多个动作的发生。通过在spin-wait循环结构的每次迭代中调用此方法,调用线程会向运行时指示它正在等待忙碌。运行时可能会采取行动来提高调用自旋等待循环结构的性能。

查看JEP 285了解更多详情。