简体   繁体   English

Java - 主方法中的Thread.sleep

[英]Java - Thread.sleep in the main method

I'm reviewing Java code that essentially is a recurring process that moves/reads/parses some files on regular basis and outputs data into the database. 我正在审查Java代码,它本质上是一个定期移动/读取/解析某些文件并将数据输出到数据库的重复进程。 The repeating part is done (roughly) as follows: 重复部分(大致)完成如下:

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

My recommendation was to refactor code to 我的建议是重构代码

  1. Create instance of Collector and refactor static execute() method to the instance method 创建Collector实例并将static execute()方法重构为实例方法
  2. To use Runnable or TimerTask to handle invocations 使用Runnable或TimerTask来处理调用

My argument was that using Thread.wait from the main method and combining it with static access is not a good way of handling repeatable process especially doing file IO. 我的论点是,从main方法使用Thread.wait并将其与静态访问相结合并不是处理可重复进程的好方法,尤其是执行文件IO。 To which the author replied (quoting) 作者回复(引用)

The description of Runnable says "should be implemented by any class whose instances are intended to be executed by a thread". Runnable的描述说“应该由任何其实例打算由线程执行的类实现”。 In fact, I am intentionally avoiding threads in this program, for reasons of cost vrs.performance requirements. 事实上,出于成本效率要求的原因,我故意避免在该程序中使用线程。

Here's another quote from same discussion which will hopefully help to clarify the author's position 这是同一个讨论的另一个引用,希望有助于澄清作者的立场

Technically, Java isn't executed at all, it is interpreted by the JVM which then executes machine instructions, to simulate that the Java code is executing. 从技术上讲,Java根本不会被执行,它由JVM解释,然后JVM执行机器指令,以模拟Java代码正在执行。 So it's really the JVM that is executing on a thread, or multiple threads. 所以它真的是在一个线程或多个线程上执行的JVM。

But as a Java code writer, I don't care. 但作为Java代码编写者,我并不在意。 If I don't create "threads" in Java, then It's the job of the JVM to appear as if there are no threads — even if the JVM is using threads "under the covers". 如果我不在Java中创建“线程”,那么JVM的工作就好像没有线程一样 - 即使JVM正在使用“隐藏的线程”。

A Java Pause is not executed, it is simulated by a sequence of machine instructions that may or may not call an OS 'wait'. Java Pause未执行,它由一系列机器指令模拟,这些指令可能会也可能不会调用操作系统“等待”。 (It probably does, because the JVM would not want to spin, burning CPU cycles, but that's a JVM implementation choice). (它可能会,因为JVM不想旋转,燃烧CPU周期,但这是一个JVM实现选择)。

So I have 2 questions: 所以我有两个问题:

  1. Is putting Thread.wait into the main method legit, safe and advisable way of doing repeatable task in this instance? 是否将Thread.wait置于主要方法中,在此实例中执行可重复任务的合法,安全和可取的方法是什么? And if not, why not since there's only one (main) thread of execution? 如果没有,为什么不,因为只有一个(主要)执行线程?
  2. What are the pitfals of using static metods in this context (if any)? 在这种情况下使用静态方法(如果有的话)有什么问题?

I'll be happy to provide additional info if you have any other questions. 如果您有任何其他问题,我将很乐意提供其他信息。

You're really arguing about design decisions, not performance decisions. 你真的在争论设计决策而不是绩效决策。

Your colleague's statement about how Thread.sleep is implemented is basically incorrect as far as I can see. 就我所见,你的同事关于如何实现Thread.sleep的声明基本上是不正确的。 On a sensible JVM on a sensible operating system, Thread.sleep() is implemented using an O/S native method for parking a thread (or putting it in a "timed wait" state or whatever you want to call it on your OS). 在合理的操作系统上的合理JVM上,Thread.sleep()是使用O / S本机方法实现的,用于停止线程(或将其置于“定时等待”状态或您想在操作系统上调用它的任何内容) 。 Or put another way, while a thread is sleeping, it consumes zero CPU. 换句话说,当线程处于休眠状态时,它会占用零CPU。 And in any case, TimerTask will use Thread.sleep (or similar-- I don't just recall if it uses the park() method introduced in Java 5, but it essentially makes no difference). 在任何情况下,TimerTask都将使用Thread.sleep(或类似 - 我不只是回忆它是否使用Java 5中引入的park()方法,但它基本上没有区别)。

The JVM won't generally make secret under-the-hood decisions about threading. JVM通常不会做出关于线程的秘密决策。 If you ask for another thread, you'll get one; 如果你要求另一个线程,你会得到一个; if you don't, you won't. 如果你不这样做,你就不会。 A few "housekeeping" threads will be created for garbage collection etc, but as far as your code is concerned, you can assume that no secret clever thread creation is going on. 将为垃圾收集等创建一些“内务”线程,但就您的代码而言,您可以假设没有秘密聪明的线程创建正在进行。

So, coming back to your questions: 那么,回到你的问题:

  • sleeping in the main thread is perfectly safe per se; 睡在主线上本身是完全安全的; obviously, while the main thread is sleeping, it won't be executing anything, but if you don't need it to, then that's fine; 很明显,当主线程处于休眠状态时,它不会执行任何操作,但如果您不需要它,那么这很好;
  • whether you use Thread.sleep() directly in your code or use one of the "timer" utility methods is again a design decision based on whether you want to use a general library that removes implementational details from your code, or whether you prefer to have those details present and under your control; 是否直接在代码中使用Thread.sleep()或使用“计时器”实用程序方法之一再次是一个设计决定,取决于您是否要使用从代码中删除实现细节的通用库,或者您是否愿意将这些细节呈现在您的控制之下; performance-wise, it will make little odds if you implement things correctly; 在性能方面,如果你正确地实施事情,它将几乎没有可能;
  • whether you have a static method or have an instance of Collector doesn't essentially matter-- it's just a design decision that you need to make based on what seems more intuitive: do you see Collector as a "general utility" class, or as a "thing" with state that you request to perform operations? 无论你有一个静态方法还是有一个收集器实例并不重要 - 它只是一个你需要根据看起来更直观的设计决策:你看到收集器是一个“通用实用程序”类,还是您要求执行操作的状态“事物”?

You're confusing sleep and wait . 你困惑的sleepwait sleep makes the current thread idle for some period of time, and then the thread restarts on its own. sleep使当前线程空闲一段时间,然后线程自行重启。 wait is a method of Object. wait是Object的一种方法。 It's used to put a thread in a waiting state, and it will only go out of this state if another thread wakes him up using notify or notifyAll on the same object. 它用于将线程置于等待状态,并且如果另一个线程在同一对象上使用notifynotifyAll唤醒它,它将只会退出此状态。 Using wait if there's only one thread executing will just make your program hang eternally. 如果只执行一个线程,则使用wait将使程序永久挂起。

Read http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html 阅读http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html

I think your colleague doesn't really understand the JVM and its threading model; 我认为您的同事并不真正了解JVM及其线程模型; your code is not going to magically multi-thread unless you explicitly make it that way. 你的代码不会神奇地多线程,除非你明确地这样做。 Further, I think both of you are working too hard at this. 此外,我认为你们两个都在努力工作。 Give the Quartz library a try: http://quartz-scheduler.org/documentation/quartz-2.1.x/quick-start 试试Quartz库: http//quartz-scheduler.org/documentation/quartz-2.1.x/quick-start

My reasoning is that Java Threading is difficult to get right, especially when you find yourself doing wait/notify on your own, and grappling with all of the edge cases. 我的理由是Java Threading很难做到正确,特别是当你发现自己正在进行等待/通知时,并且正在努力解决所有边缘情况。 The quartz library has abstracted this all away and put the repetitive aspect behind the familiar CRON pattern. 石英库将这一切都抽象出来,并将重复的方面置于熟悉的CRON模式之后。

I would totally avoid TimerTask as it has the nasty habit of silently failing if there are any unhandled exceptions that occur during your run. 我完全会避免使用TimerTask,因为如果在运行期间发生任何未处理的异常,它会有一种悄悄失败的恶习。

Calling static methods or not is no big deal regardless of your solution, the key is understanding what state gets shared across threads and either eliminate the sharing OR synchronize the access to it so all Threads get a consistent view of the data. 无论您是否使用解决方案,调用静态方法都没有什么大不了的,关键是要了解哪些状态在线程之间共享,要么消除共享,要么同步对它的访问,以便所有线程获得一致的数据视图。 If I was in your position, I would give Quartz a shot. 如果我在你的位置,我会给Quartz一个机会。 If you are reluctant to add yet another library, JDK 1.5 (I think) brought in the ScheduledExectutorService which I think does repetitive tasks. 如果您不愿意添加另一个库,JDK 1.5(我认为)引入了ScheduledExectutorService,我认为它可以执行重复的任务。

No matter which way you go, don't write the scheduling/exectution framework yourself. 无论你走哪条路,都不要自己编写调度/执行框架。 This is a solved problem in Quartz or the ScheduledExectutorService. 这是Quartz或ScheduledExectutorService中已解决的问题。

In the code sample posted, I would use Object.wait(60 * 1000) instead of Thread.sleep(60 * 1000) , then within the shutdown method, add a call to notifyAll() after setting signalReceived = true . 在发布的代码示例中,我将使用Object.wait(60 * 1000)而不是Thread.sleep(60 * 1000) ,然后在shutdown方法中,在设置signalReceived = true后添加对notifyAll()的调用。 This will require adding the necessary synchronization blocks around both the notify and the wait methods. 这将需要在notify和wait方法周围添加必要的同步块。 At a minimum, this will provide the benefit of the "wait loop" being able to exit immediately, rather than waiting for the timeout to elapse first. 至少,这将提供“等待循环”能够立即退出的好处,而不是等待超时首先经过。 (See JB Nizet's answer for some additional detail around this.) (有关此问题的一些其他详细信息,请参阅JB Nizet的答案。)

From an overall perspective - I'd have the Collector implement TimerTask (sub-interface of Runnable ), schedule it with a Timer , and be done with it. 从整体的角度来看 - 我有Collector实现TimerTaskRunnable子接口),用Timer安排它,并完成它。

Some users will advocate the use of static for performance reasons, which I would argue to be mostly historical. 一些用户会出于性能原因提倡使用静态,我认为这主要是历史性的。 Keeping everything non-static will allow for future use of multiple instances within the same JVM, as well as being able to use subclasses to override methods and customize the base implementation. 将所有内容保持为非静态将允许将来在同一JVM中使用多个实例,以及能够使用子类来覆盖方法和自定义基本实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM