Throttling Tasks

Throttling Tasks #

Finally, we look at a class used by the grid coloring application that is useful in other contexts too. To implement the animation, the coloring algorithm notifies the image canvas to rerender the image when colors in the grid change. However, when too many notifications arrive in a short time the user interface thread becomes unresponsive. To avoid overloading the user interface thread with notifications we use a class for throttling tasks represented as Runnable instances. Throttling is implemented by delaying executed tasks and ignoring subsequent executions until the delay is over.

The ColoringApp uses the following line to register a throttled rendering task to be executed by the coloring algorithm.

coloring.onChange(new Throttled(canvas::rerender, 200, TimeUnit.MILLISECONDS));

The rerender method does not actually perform the rendering. It only signals to the user interface thread that rerendering is necessary.

The Throttled class stores the arguments passed in the costructor and has a run method that is implemented as follows.

public synchronized void run() {
    if (!delayed) {
        delayed = true;
        service.schedule(() -> {
            command.run();
            delayed = false;
        }, delay, unit);
    }
}

The run method is synchronized to prevent different threads from executing it at the same time. The delayed flag signifies whether the current invocation should be ignored because a previous invocation has already been delayed. If the current invocation is not ignored it is delayed according to the delay passed in the constructor. The service used to execute the delayed task is an instance of ScheduledExecutionService initialized in the constructor.