Maria1995
Maria1995

Reputation: 501

Execute two threads in parallel and restart the first one when it ends instead of waiting for both to finish

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

Answers (1)

Charlie Armstrong
Charlie Armstrong

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

Related Questions