The guideline to execute a long running operation in a separate thread is well known, especially for GUI triggered stuff. However I did not come across any best practice to deal with lengthy operations creeping. For example scrolling faster then the content refresh rate will cause a series of content updates going on and on long after the user has finished scrolling. This post is to provide a reference solution to handle such a case.
The LazyRunner
, I think I can call it a pattern, guarantees that only the latest Runnable
submitted to the execute()
method will actually run (unless external disruption such as thread interruption or system error happens). All the Runnables
submitted while some other Runnable
is being executed are ignored.
In result the maximum time to finish processing after last execution request is equal to
2 * <single runnable time> instead of
<number of requests> * <single runnable time>.
public class LazyRunner { private volatile Runnable running; private volatile Runnable latest; public final void execute(final Runnable r) { latest = r; if (running == null) { running = latest; new Thread("lazy runner") { @Override public void run() { do { running = latest; running.run(); } while (running != latest); running = null; }; }.start(); } } }
An example of usage:
static class LongOperation implements Runnable { String s; public LongOperation(String s) { this.s = s; } @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) {} System.out.println(s); } } public static void main(String[] args) throws InterruptedException { LazyRunner runner = new LazyRunner(); runner.execute(new LongOperation("a")); runner.execute(new LongOperation("b")); Thread.sleep(100); runner.execute(new LongOperation("c")); Thread.sleep(100); runner.execute(new LongOperation("d")); Thread.sleep(100); runner.execute(new LongOperation("e")); }
The output should print “b” and “e”.
A special case is slow SWT operations, for instance drawing text in GTK2 on Linux, which causes a noticeable rendering delay for StyledText
with a few dozen of lines. A LazyRunnable
in such scenario has to mingle with display.asyncExec()
method to execute the Runnable
in the GUI thread. It can be implemented as follows:
public class SwtLazyRunner { private final Display display; private volatile Runnable running; private volatile Runnable latest; public SwtLazyRunner(Display display) { this.display = display; } public final void execute(final Runnable r) { latest = r; if (running == null) { running = latest; if (!display.isDisposed()) { display.asyncExec(new Runnable() { @Override public void run() { do { running = latest; running.run(); } while (running != latest); running = null; } }); } } } }
I hope you find it useful.