Reputation: 501
I have two methods in Java and I execute them in parallel in a class which has a fixed delay. The first thread takes a few minutes to complete, while the second one can take some hours. What I want is to restart the first thread and execute it when it ends, instead of waiting for the second one to finish and re-execute both of them. Can anyone help me with this? My code is below:
@Scheduled(fixedDelay = 30)
public void scheduled_function() throws IOException, InterruptedException {
Callable<Void> callableSchedule = new Callable<Void>()
{
@Override
public Void call() throws Exception
{
getAndUpdateSchedule();
return null;
}
};
Callable<Void> callableMatches = new Callable<Void>()
{
@Override
public Void call() throws Exception
{
processMatches();
return null;
}
};
//add to a list
List<Callable<Void>> taskList = new ArrayList<Callable<Void>>();
taskList.add(callableSchedule);
taskList.add(callableMatches);
//create a pool executor with threads
ExecutorService executor = Executors.newFixedThreadPool(2);
try
{
//start the threads
executor.invokeAll(taskList);
}
catch (InterruptedException ie)
{
System.out.println("An InterruptedException occured");
}
Upvotes: 1
Views: 300
Reputation: 2342
You can just store a boolean variable, let's call it isComplete
, that stores whether the long task has completed or not. This will be an instance variable, since we need it to stay around after scheduled_function()
returns. Something like this:
private boolean isComplete = false;
Now, right now this variable is meaningless because we never update it. So, we need to make sure to update this variable when the long task completes:
Callable<Void> callableMatches = new Callable<Void>()
{
@Override
public Void call() throws Exception
{
processMatches();
synchronized (MyClass.this) { // MyClass is just a placeholder name
isComplete = true;
}
return null;
}
};
Notice that where I update the isComplete
variable, I put it in a synchronized block. This ensures that the value we are writing is actually going to be updated on the other thread, and it prevents the other thread from reading while we're writing the value. The result is that the other thread always gets the updated value.
This bit is tangential to the answer, but we can actually shorten this piece of code significantly by using lambda syntax. Callable
is a functional interface, so this is perfectly legal:
Callable<Void> callableMatches = () -> {
processMatches();
synchronized (MyClass.this) { // MyClass is just a placeholder name
isComplete = true;
}
return null;
};
Now all we have to do is check this variable every time we want to start the short task. Since we only have 2 threads, and one of the threads is being used for the long task, we know that this task will always be executed on the same thread. This means there's no point in going back to the executor, we can just put it in a while loop inside the callable. On every iteration of the while loop, we just need to check our isComplete
variable, and we'll break out of the loop if the other task has completed.
Callable<Void> callableSchedule = () -> {
while (true) {
synchronized (MyClass.this) { // MyClass is just a placeholder name
if (isComplete) {
break;
}
}
getAndUpdateSchedule();
}
return null;
};
Note that in this example, I've used the lambda syntax and I've put the if statement inside another synchronized block. As I explained above, we don't want to get a stale value here and keep looping after the other task is complete.
Upvotes: 2