Consider a Swing application with a JList or JTable, when the selection changes a SwingWorker is started and loads related data from database and updates UI. This works fine and the UI is responsive.
But if the user is quickly changing the selected row (holding key-up/down) I want to be sure that the last selected row is the one that is loaded last, and also I don't wanna query the DB in vain. So what I want is an single threaded Executor with a LIFO queue of size=1. So submitting a task to it removes any previous submitted tasks and making it execute at most 1 task at a time and having at most 1 task waiting for execution.
I couldn't find anything like this in java.util.concurrent so I wrote my own Executor. Was I right in doing that or am I missing something from the concurrent package? Is the solution acceptable or is there better ways of achieving what I want?
public class SingleLIFOExecutor implements Executor
{
private final ThreadPoolExecutor executor;
private Runnable lastCommand;
public SingleLIFOExecutor()
{
executor = new ThreadPoolExecutor(0, 1, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1));
}
@Override
public void execute(Runnable command)
{
executor.remove(lastCommand);
lastCommand = command;
executor.execute(command);
}
}
And here's an example showing how it could be used:
final Executor executor = new SingleLIFOExecutor();
JList jList = createMyList();
jList.addListSelectionListener(new ListSelectionListener()
{
@Override
public void valueChanged(ListSelectionEvent e)
{
if (!e.getValueIsAdjusting())
{
executor.execute(new MyWorker());
}
}
});
This was the solution I implemented, works great for the problem I tried to solve :)
/**
* A "Single Last-In-First-Out Executor".
* <p>
* It maintains a queue of <b>one</b> task and only one task may execute simultaneously,
* submitting a new task to {@link #execute(Runnable)} will discard any previous submitted not yet started tasks.
*/
public class SingleLIFOExecutor implements Executor
{
private final ThreadPoolExecutor executor;
private Runnable lastCommand;
public SingleLIFOExecutor()
{
executor = new ThreadPoolExecutor(0, 1, 0, MILLISECONDS, new ArrayBlockingQueue<Runnable>(1));
}
/**
* @see java.util.concurrent.Executor#execute(java.lang.Runnable)
*/
@Override
public void execute(Runnable command)
{
executor.remove(lastCommand);
lastCommand = command;
executor.execute(command);
}
}