I wrote a lazy image downloader for my app using an ExecutorService. It gives me great control about how many downloads are running in parallel at what time and so on.
You will need to specify the queue type that the ExecutorService is using.
Typically you might be retrieving an ExecutorService via the static methods in Executors. Instead you will need to instantiate one directly and pass in the Queue type that you want that provides LIFO.
EG, to create a LIFO thread pool executor, you could use the following constructor.
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)
and pass in a LIFO queue as the final parameter.
There is no LIFO queue in the java collections that I am aware of (please correct me if wrong), but you could easily just create an anonymous inner class that extends LinkedBlockingQueue and overrides the appropriate methods.
For example, (untested)
ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 16, 1, TimeUnit.MINUTES, new LinkedBlockingQueue() {
@Override
public void put(Object obj) {
// override to put objects at the front of the list
super.addFirst(obj);
}
});
UPDATE in response to comments.
We can use a blocking queue that wraps a priority queue. We have to wrap because the Executor expects runnables but we need timestamps too.
// the class that will wrap the runnables
static class Pair {
long timestamp;
Runnable runnable;
Pair(Runnable r) {
this.timestamp = System.currentTimeMillis();
this.runnable = r;
}
}
ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 16, 1, TimeUnit.MINUTES, new BlockingQueue() {
private Comparator comparator = new Comparator() {
@Override
public int compare(Pair arg0, Pair arg1) {
Long t1 = arg0.timestamp;
Long t2 = arg1.timestamp;
// compare in reverse to get oldest first. Could also do
// -t1.compareTo(t2);
return t2.compareTo(t1);
}
};
private PriorityBlockingQueue backingQueue = new PriorityBlockingQueue(11, comparator);
@Override
public boolean add(Runnable r) {
return backingQueue.add(new Pair(r));
}
@Override
public boolean offer(Runnable r) {
return backingQueue.offer(new Pair(r));
}
@Override
public boolean offer(Runnable r, long timeout, TimeUnit unit) {
return backingQueue.offer(new Pair(r), timeout, unit);
}
// implement / delegate rest of methods to the backing queue
});