0

我正在尝试编写一个每 3 分钟运行一次的简单 Java 程序。我正在使用 Timer 和 TimerTask 来调用这些类。

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class Receiver
{
    public static void main(String[] args)
    {
        System.out.println("Time now is -> " + new Date());

        Timer timer = new Timer();
        TimerTask task = new ReceiverTask();
        timer.scheduleAtFixedRate(task, 0, 180000);
    }
}

class DPGReceiverTask extends TimerTask
{
    private ArrayList<TaskArgs> m_tasks = new ArrayList<TaskArgs>();

    public ReceiverTask()
    {
        m_tasks.add(new TaskArgs("com.comp.Receiver", new String[] { "ARG1", "ARG2"}));
    }

    public void run()
    {
        System.out.println("Receiver Started!");

        String classpath = "D:/Receiver;D:/Receiver/lib/*";
        int i = 0;

        ArrayList<Process> processes = new ArrayList<Process>();
        for (TaskArgs task: m_tasks)
        {
            try
            {
                List<String> command = new ArrayList<String>();
                command.add(System.getProperty("java.home") + "/bin/java");
                command.add("-classpath");
                command.add(classpath);
                command.add(task.Name);

                String[] args = task.Args;
                for (String arg : args)
                {
                    command.add(arg);
                }

                Process process = new ProcessBuilder(command).start();
                processes.add(process);

            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        try
        {
            for (Process process : processes)
            {
                int exitCode = process.waitFor();
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}
public class TaskArgs
{
    public TaskArgs(String name, String[] args)
    {
        Name = name;
        Args = args;
    }

    public String Name;
    public String[] Args;
}

我实现winsw并创建了运行 bat 文件的服务,而后者又运行 java:

接收任务.bat

java -classpath ReceiverService.jar Receiver

接收器任务.xml

<service>
  <id>receiverTask</id>
  <name>receiverTask</name>
  <description>receiver Service</description>
  <executable>receiverTask.bat</executable>
  <logpath>D:\winsw\logs\Service</logpath>
 <log mode="roll-by-time">
  <pattern>yyyyMMdd</pattern>
    </log>
  <depend>Spooler</depend>
  <startargument>run</startargument>
  <stopargument>stop</stopargument>
</service>

我在当前的实现中遇到了两个问题:

  1. 无论当前java是否完成,Timer都会在3分钟后启动另一个进程。
  2. 停止服务(winsw - java 服务包装器)不会停止运行 java.exe 或 cmd.exe 的进程

我尝试添加销毁方法来杀死进程,但这真的是要走的路吗?

        ...    
        try
        {
            for (Process process : processes)
            {
                Timer t = new Timer();
                TimerTask killer = new TimeoutProcessKiller(process);
                t.schedule(killer, 178000);
                int exitCode = process.waitFor();
                killer.cancel();
            }
        }

public class TimeoutProcessKiller extends TimerTask
{
    private Process p;
    public TimeoutProcessKiller(Process p)
    {
        this.p = p;
    }

    public void run()
    {
        p.destroy();
    }
}

有没有人有什么建议?

4

2 回答 2

1

如果您在 Java 应用程序中通过调用 ProcessBuilder.start() 来启动进程,那么您就有一个对它的有效 Process 引用,并且您可以调用 Process 类中的 destroy() 方法来终止该特定进程。所以,你应该在创建新进程之前遍历你的列表,如果有的话就销毁它。

class DPGReceiverTask extends TimerTask {
    private ArrayList<TaskArgs> m_tasks = new ArrayList<TaskArgs>();
    private LinkedList<Process> processes = new LinkedList<Process>();

    public ReceiverTask() {
        m_tasks.add(new TaskArgs("com.comp.Receiver", new String[] { "ARG1", "ARG2"}));
    }

    public void run() {
        // shutdown prev. created processes
        for (Process process: processes) {
            process.destroy();
        }
        processes.clear();

        System.out.println("Receiver Started!");

        String classpath = "D:/Receiver;D:/Receiver/lib/*";
        int i = 0;

        for (TaskArgs task: m_tasks) {
            try {
                List<String> command = new ArrayList<String>();
                command.add(System.getProperty("java.home") + "/bin/java");
                command.add("-classpath");
                command.add(classpath);
                command.add(task.Name);

                String[] args = task.Args;
                for (String arg : args)
                {
                    command.add(arg);
                }

                Process process = new ProcessBuilder(command).start();
                processes.add(process);

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
于 2016-04-13T18:30:34.373 回答
1

使用 aTimer是一种相对粗略的方式来进行这种调度。

该类文档的第七段甚至建议使用下面描述的替代方法。

Java 5.0 引入了 java.util.concurrent 包……用于以给定的速率或延迟重复执行任务……更通用的 Timer/TimerTask 替代品……</p>

执行者

Java 5 及更高版本提供了用于处理此类任务的Executor 框架。

具体来说,您想要ScheduledExecutorService. 这解决了你的两个问题。

  • 在执行下一个作业之前,该服务会一直等待,直到挂起的作业完成。
  • 该服务为您提供了一个ScheduledFuture对象,您可以通过该对象检查其状态并停止进一步的工作。该cancel方法允许您指定是要等到当前作业完成还是尝试中断当前作业。

学习

在 Stack Overflow 上搜索许多使用ScheduledExecutorService.

您将需要一点时间和精力来将头绕在移动部件上。但是,将时间花在您的应用程序中生成的代码是一种简单而优雅的方式来处理管理线程后台作业的挑战。

我不会从这里开始阅读,但稍后您可能会发现我的这个答案很有帮助,其中包含一个ScheduledExecutorService.

警告

当心一个关键技巧:在/对象的/方法的外部使用 a 包围,CallableRunnablecallruntry-catch因为任何到达服务的未捕获异常都会导致服务停止安排进一步的作业。这种停工功能会悄无声息地发生。此行为是一项功能,而不是错误。

解决方法很简单,可以在 Callable/Runnable 对象中捕获任何Exception(甚至可能是任何Throwable)——无论如何,这都是有道理的,因为任何未捕获的异常达到该级别意味着出现问题,您希望了解并编辑您的代码,以便在您的实施中进一步正确处理。

有关详细信息,请参阅此问题:ScheduledExecutorService 异常处理

于 2016-04-13T22:08:15.423 回答