如何使用一个Java程序来监视另一个Java程序的输出?

下面的图表显示了我正在尝试做的事情:它只是2个程序。 一个是简单的Child ,它每2秒逐行写出整数。

另一个是监视日志文件的Parent程序(只是一个非常基本的文本文件)。 如果日志文件在5秒内没有被修改,那么它应该重启Child (通过批处理文件); 然后继续正常。

在此处输入图像描述

我的子类代码在这里:

 package fileiotestapplication; import java.io.*; import java.io.IOException; import java.util.*; public class WriterClass { @SuppressWarnings("oracle.jdeveloper.java.insufficient-catch-block") public WriterClass() { super(); int[] content = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,}; String[] friends = {"bob",}; File file = new File("/C:/Java_Scratch/someFile.txt"); // if file does not exists, then create it try { if (!file.exists()) { file.createNewFile(); } for (int i = 0 ; i < content.length; i++) { PrintStream bw = new PrintStream( new FileOutputStream(file, true) ); System.out.println("testing " + i); bw.println( String.valueOf(content[i]) ); bw.close(); Thread.sleep(2500); } System.out.println("Done"); } catch (IOException ioe) { // TODO: Add catch code ioe.printStackTrace(); } catch (InterruptedException ioe) { // TODO: Add catch code ioe.printStackTrace(); } //someIS.println(i); System.out.println("This is OK"); } public static void main(String[] args) { WriterClass writerClass = new WriterClass(); } } 

源代码

我在这里链接了我父类的当前代码 。

我现在要做的是添加一些在子类停止写输出时捕获的逻辑。 我想做的是计算日志文件中的所有行; 然后每5秒比较一次,这是一个好方法(替代方法是 – 继续检查以查看文件是否完全修改)?

编辑:下面的建议使用waitFor()确实有帮助,虽然我仍在研究细节:它通常像:

  try { /* StackOverflow code */ for ( ; ; ) { ProcessBuilder pb = new ProcessBuilder("TheBatchFile.bat"); pb.directory(new File("C://Java_Scratch_//Autonomic_Using_Batch//")); Process p = pb.start(); p.waitFor(); } /* end - StackOverflow code */ } catch (IOException i) { i.printStackTrace(); } catch (InterruptedException i) { i.printStackTrace(); } 

虽然您创建文本文件和使用批处理脚本的方法是可行的,但有一种更好的方法可以实现。 这是多任务处理的标准问题,通过创建几个线程,它根本不是很难。

使用线程与使用批处理文件和多个程序在系统外部“周围”相比具有几个优点。 首先,这些可能包括:

  1. 将所有内容保持在一起使项目更加整洁,清洁,并且更容易分发。

  2. 它更容易实现。 如果你从来没有使用它们,肯定的线程可能会让人感到困惑,但在我看来它们是较小的邪恶,然后是绕过它们的所有步骤。 正如我希望在下面展示的那样,用线程实现这个问题并不难。

  3. 由于避免了文件IO的非常昂贵的操作以及产生批处理文件,因此提高了性能。 在大多数情况下,线程也比流程提高了性能,因为它们更容易产生,而multithreading通过减少对多个内核的依赖,可以在更广泛的处理器上实现性能提升而不是多处理。

  4. 当一个程序正在读取文件而另一个程序同时写入文件时,之间没有粗略的重叠。 在可能的情况下,最好避免这种情况。

  5. 保持Java令人印象深刻的跨平台能力,因为您没有使用非跨平台的批处理。 对于这个项目来说,这对你来说可能并不重要,但是你可能会遇到类似问题的未来,这一点更为重要,因此你将练习实现它。

  6. 通过使用线程“正确的方式”而不是通过使用更加hacky方法来养成坏习惯,你可以更好地学习。 如果这是一个学习项目,你也可以正确地学习它。

我继续编写了我最有可能用来解决问题的方法。 我的代码有一个子线程每两秒计数一次,以及一个监视子进程的父线程,如果子进程没有计数就会重新启动它。 让我们检查一下我的程序,让你很好地了解它的工作原理。

首先,这是父级的类:

 public class Parent { private Child child; public Parent(){ child = new Child(this); child.start(); } public void report(int count){ //Starts a new watchdog timer Watchdog restartTimer = new Watchdog(this, count); restartTimer.start(); } public void restartChild(int currentCount){ if (currentCount == child.getCount()){ //Check if the count has not changed //If it hasn't child.kill(); child.start(); } } public static void main(String[] args){ //Start up the parent function, it spawns the child new Parent(); } } 

如果你想要的话,那里的主要function可以放在其他地方,但要开始一切,只需实例化一个父。 父类具有子类的实例,并启动子线程。 孩子将通过报告方法向父母报告它正在计数,该方法产生一个看门狗定时器(在一秒钟内更多)将在五秒后用当前计数调用restartChild。 RestartChild,如果计数仍然与提供的计数相同,则重新启动子线程。

这是监视程序计时器的类:

 class Watchdog implements Runnable { //A timer that will run after five seconds private Thread t; private Parent parent; private int initialCount; public Watchdog(Parent parent, int count){ //make a timer with a count, and access to the parent initialCount = count; this.parent = parent; } public void run() { //Timers logic try { Thread.sleep(5000); // If you want to change the time requirement, modify it here parent.restartChild(initialCount); } catch (InterruptedException e) { System.out.println("Error in watchdog thread"); } } public void start () // start the timer { if (t == null) { t = new Thread (this); t.start (); } } } 

此监视程序计时器是父计算机将使用start方法运行的线程。 父节点将自身作为参数发送,以便我们可以调用父节点的restartChild函数。它存储计数,因为当它在五秒后运行时,restartChild将检查计数是否已更改。

最后,这是儿童class

 public class Child implements Runnable{ private Thread t; public int counter = 0; private boolean running; private Parent parent; // Record the parent function public Child(Parent parent){ this.parent = parent; } private void initializeAll(){ counter = 0; running = true; } public int getCount(){ return counter; } @Override public void run() { while((counter <= 100)&&(running)){ //The main logic for child counter +=1; System.out.println(counter); parent.report(counter); // Report a new count every two seconds try { Thread.sleep(2000); // Wait two seconds } catch (InterruptedException e) { System.out.println("Thread Failed"); } } } public void start(){ //Start the thread initializeAll(); t = new Thread(this); t.start(); } public void kill(){ //Kill the thread running = false; } } 

这也是一个线程,因此它实现了runnable,并且在这方面的行为很像看门狗。 Run()是子线程的主要方法,这是您启动它时调用的逻辑。 使用start()启动子项将所有变量设置为其默认值,然后开始运行run()逻辑。 运行中的逻辑包含在if(running)中,因为这可以让我们通过将运行设置为false来内部杀死线程。

目前,所有孩子现在正在增加它的计数器,将其输出到控制台,然后每隔两秒向父母报告100次活动。 您可能希望删除在计数超过100后停止它的条件,但我将其包括在内,以便父级最终可能会重新启动子级。 要更改行为,请查看子进程的run方法,即所有主要操作所在的方法。

随着文件大小的增加,这将变得非常慢。 一种更简单的方法是简单地检查文件的最后修改时间。 假设子程序可能停止写入文件的原因是程序终止(而不是例如挂在无限循环中),最好直接监视子进程本身而不是依赖于观察进程的影响。 如果父进程可以首先负责启动程序,这将特别方便。

这可以通过Java 8中的ProcessBuilder和Process类完成。从文档中复制,您可以像这样启动流程(如果您只想监视它是否正在运行):

 ProcessBuilder pb = new ProcessBuilder("TheBatchFile.bat", "Argument1", "Argument2"); pb.directory(new File("/path/to/working/dir")); Process p = pb.start(); 

然后,您只需调用p.waitFor(); 等待进程终止。 在循环中执行此操作,您将自动重新启动子行为。

您可以使用目录监视服务:

https://docs.oracle.com/javase/tutorial/essential/io/notification.html

您可以配置路径或文件并注册watcher

每次更改文件时,观察者都会收到通知。 您可以存储此通知的时间戳以供以后使用。

有关详情,请参阅上面的链接。

然后,您可以使用Timer或Thread来检查上次修改。