我正在审查Java代码,它本质上是一个定期移动/读取/解析某些文件并将数据输出到数据库的重复过程。重复部分(大致)完成如下:
public class CollectorMain {
public static boolean signalRecieved = false;
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
shutdown();
}});
while(!signalRecieved) {
Collector.execute();
try {
Thread.sleep(60 * 1000);
} catch (InterruptedException e) {
break;
}
}
// some shutdown logic
}
public static void shutdown() {
signalReceived = true;
}
}
public class Collector() {
public static void execute() {
// Move files from the queue dir to temp location
// Read, parse files and insert records into database.
// Then delete the processed files
}
}
我的建议是将代码重构为
我的论点是,从main方法使用Thread.wait并将其与静态访问相结合并不是处理可重复进程的好方法,尤其是执行文件IO。作者回复(引用)
Runnable的描述说“应该由任何类实现 其实例旨在由一个线程执行“。事实上,我 由于成本原因,我故意避免在这个程序中使用线程 vrs.performance要求。
以下是同一讨论的另一个引用,希望有助于澄清作者的立场
从技术上讲,Java根本不执行,它由JVM解释 然后执行机器指令,模拟Java 代码正在执行。所以它真的是在a上执行的JVM 线程或多个线程。
但作为Java代码编写者,我并不在意。如果我不创建“线程” 在Java中,那么JVM的工作就好像没有了 线程 - 即使JVM使用线程“隐藏”。
未执行Java Pause,它由一系列机器模拟 可能会或可能不会调用操作系统“等待”的指令。 (它可能会, 因为JVM不想旋转,燃烧CPU周期,但那就是 JVM实现选择)。
所以我有两个问题:
Thread.wait
置于主要方法中是否是合法,安全且可取的可重复任务方式?如果没有,为什么不,因为只有一个(主要)执行线程?如果您有任何其他问题,我很乐意提供其他信息。
答案 0 :(得分:6)
你真的在争论设计决策,而不是绩效决策。
就我所见,你的同事关于如何实现Thread.sleep的声明基本上是不正确的。在合理的操作系统上的合理JVM上,Thread.sleep()是使用O / S本机方法实现的,用于停止线程(或将其置于“定时等待”状态或您想在操作系统上调用它的任何内容) 。换句话说,当线程处于休眠状态时,它会占用零CPU。在任何情况下,TimerTask都将使用Thread.sleep(或类似的 - 我不只是回忆它是否使用Java 5中引入的park()方法,但它基本没有区别)。
JVM通常不会做出关于线程的秘密决策。如果你要求另一个线程,你会得到一个;如果你不这样做,你就不会。将为垃圾收集等创建一些“管家”线程,但就您的代码而言,您可以假设没有秘密聪明的线程创建。
所以,回到你的问题:
答案 1 :(得分:4)
你让sleep
和wait
感到困惑。 sleep
使当前线程空闲一段时间,然后线程自行重启。 wait
是Object的一种方法。它用于将线程置于等待状态,并且如果另一个线程在同一对象上使用notify
或notifyAll
唤醒它,它将只会退出此状态。如果只执行一个线程,则使用wait
将使程序永久挂起。
阅读http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html
答案 2 :(得分:1)
我认为您的同事并不真正了解JVM及其线程模型;你的代码不会神奇地多线程,除非你明确地这样做。此外,我认为你们两个都在努力工作。尝试使用Quartz库:http://quartz-scheduler.org/documentation/quartz-2.1.x/quick-start
我的理由是Java Threading很难做到,特别是当你发现自己正在进行等待/通知时,并且正在努力解决所有边缘情况。石英库将这一切都抽象出来,并将重复的方面置于熟悉的CRON模式之后。
我会完全避免使用TimerTask,因为如果在你的运行过程中发生任何未处理的异常,它会有一种悄悄失败的恶习。
无论您是否使用解决方案,调用静态方法都没有什么大不了的,关键是要了解哪些状态在线程之间共享,要么消除共享,要么同步对它的访问,以便所有线程获得一致的数据视图。如果我在你的位置,我会给Quartz一个机会。如果您不愿意添加另一个库,JDK 1.5(我认为)引入了ScheduledExectutorService,我认为它会重复执行任务。
无论你走哪条路,都不要自己编写调度/执行框架。这是Quartz或ScheduledExectutorService中已解决的问题。
答案 3 :(得分:0)
在发布的代码示例中,我会使用Object.wait(60 * 1000)
代替Thread.sleep(60 * 1000)
,然后在shutdown
方法中,在设置notifyAll()
后添加对signalReceived = true
的调用}。这将需要在notify和wait方法周围添加必要的同步块。至少,这将提供“等待循环”能够立即退出的好处,而不是等待超时首先经过。 (请参阅JB Nizet关于此的一些其他细节的答案。)
从整体的角度来看 - 我有收集器实现TimerTask
({3}}的子接口),使用Runnable
安排它,并完成它。
有些用户会出于性能原因提倡使用静态,我认为这主要是历史性的。将所有内容保持为非静态将允许将来在同一JVM中使用多个实例,并且能够使用子类来覆盖方法并自定义基本实现。