【问题标题】:ScheduledExecutorService execute tasks late, with low CPU & RAM usageScheduledExecutorService 执行任务较晚,CPU 和 RAM 使用率低
【发布时间】:2018-01-14 22:46:09
【问题描述】:

我需要创建多个任务,每个n秒执行一次。我决定使用ScheduledExecutorService 来安排任务执行。问题是任务没有及时执行。我认为原因是处理器时间不够,但实际 CPU 使用率约为 4-5%

我的调度器创建者:

class SchedulersCreator {

    private final ScheduledExecutorService scheduler
            = Executors.newScheduledThreadPool(1);

    public SchedulersCreator(int tasksAmount, int repeatCount) {
        for (int taskId = 0; taskId <= tasksAmount; taskId++) {
            // create new task, that executes every 2 seconds
            MyTask task = new MyTask(scheduler, repeatCount, 2, taskId);
            // execute new task
            task.run();
        }
    }

    public static void main(String[] args) {
        System.out.println("Program started");
        // create & start 10 tasks, each of the executes 10 times with period 2 seconds
        SchedulersCreator scheduler = new SchedulersCreator(10, 10);
        System.out.println("All tasks created & started");
    }
}

我的任务:

class MyTask implements Runnable {

    // number of executions
    private int executesTimesLeft;
    // execution period
    private final int periodSeconds;
    // task id
    private final int id;
    // scheduler
    private ScheduledExecutorService scheduler;
    // field to measure time between executions
    private long lastExecution = 0;

    public MyTask(ScheduledExecutorService scheduler, int executes, int periodSeconds, int id) {
        this.executesTimesLeft = executes;
        this.id = id;
        this.periodSeconds = periodSeconds;
        this.scheduler = scheduler;
    }

    private void performAction() {
        long before = System.currentTimeMillis();
        long time = (before - lastExecution) % 1_000_000;
        lastExecution = before;

// Simulates useful calculations
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
        }

        long after = System.currentTimeMillis();
        if (id % 100_000 == 0) {
            long duration = after - before;
            System.out.println("Time since prev execution:\t" + time + "\t"
                    + "Task " + id + ": "
                    + executesTimesLeft + " executions lefts; "
                    + "current duration\t" + duration);
        }

    }

    @Override
    public void run() {
        // perform useful calculation in another thread
        new Thread(() -> performAction()).run();

        executesTimesLeft--;
        if (executesTimesLeft > 0) { // schedule next task execution
            scheduler.schedule(this, periodSeconds, SECONDS);
        }
    }

}

ideone 处的代码:https://ideone.com/s3iDif。 我预计执行之间的时间约为 2 秒,但实际结果是 3-4 秒。

程序输出:

...
Time since prev execution:  3028    Task 0: 2 executions lefts; current duration    1000
Time since prev execution:  4001    Task 0: 1 executions lefts; current duration    1001

【问题讨论】:

    标签: java concurrency scheduledexecutorservice


    【解决方案1】:

    您的代码没有正确使用调度程序。

    // perform useful calculation in another thread
    new Thread(() -> performAction()).run();
    

    这实际上并没有在新线程中运行代码。为此,您需要致电start(),而不是run()。调用run() 使代码在当前线程中执行,与您刚刚编写的performAction(); 没有什么不同。

    但是,您根本不应该明确地创建一个新线程。您可以而且应该在MyTask.run() 中完成这项工作。

    任务不需要知道调度程序或其频率。更改此代码:

    MyTask task = new MyTask(scheduler, repeatCount, 2, taskId);
    // execute new task
    task.run();
    

    到:

    MyTask task = new MyTask(repeatCount, taskId);
    Future<?> future = scheduler.scheduleAtFixedRate(task, 0, 2, SECONDS);
    

    您希望任务重复,因此请使用执行此操作的调度程序方法。这将允许调度程序根据任务运行的时间来调整任务之间的时间。

    将所有performAction() 移动到MyTask.run()。如果您希望任务停止重复,请使用future 取消它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-29
      • 1970-01-01
      • 1970-01-01
      • 2021-11-05
      相关资源
      最近更新 更多