I need to create multiple tasks, each of that executes every n
seconds. I've decided to use ScheduledExecutorService
to schedule task execution. The problem is tasks not executed in time. I thought the reason is not enough processor time, but actual CPU usage is about 4-5 percents.
My schedulers creator:
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");
}
}
My task:
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);
}
}
}
The code at the ideone: https://ideone.com/s3iDif. I've expected time between executions about 2 seconds, but the actual result is 3-4 seconds.
Program output:
...
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
Your code doesn't use the scheduler properly.
// perform useful calculation in another thread
new Thread(() -> performAction()).run();
This doesn't actually run the code in a new thread. To do that you need to call start()
, not run()
. Calling run()
makes the code execute in the current thread, no different than if you had just written performAction();
.
However, you shouldn't be explicitly creating a new thread at all. You can and should do the work right in MyTask.run()
.
Tasks don't need to know about the scheduler or their frequency. Change this code:
MyTask task = new MyTask(scheduler, repeatCount, 2, taskId);
// execute new task
task.run();
to:
MyTask task = new MyTask(repeatCount, taskId);
Future<?> future = scheduler.scheduleAtFixedRate(task, 0, 2, SECONDS);
You want the task to repeat, so use the scheduler method that does so. That'll allow the scheduler to adjust the time in between tasks based on how long they take to run.
Move all of performAction()
into MyTask.run()
. When you want the task to stop repeating, use the future
to cancel it.