具有LIFO订购的执行器服务

时间:2011-05-24 08:22:59

标签: android

我使用ExecutorService为我的应用编写了一个懒惰的图像下载器。它让我可以很好地控制在什么时候并行运行的下载量等等。

现在,我遇到的唯一问题是,如果我提交任务,它最终会在队列的尾部(FIFO)结束。

有谁知道如何将其更改为LIFO?

4 个答案:

答案 0 :(得分:9)

您需要指定ExecutorService正在使用的队列类型。

通常,您可能正在通过Executors中的静态方法检索ExecutorService。相反,您需要直接实例化一个并传入您想要提供LIFO的队列类型。

EG,要创建LIFO线程池执行程序,可以使用以下构造函数。

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)

并传入LIFO队列作为最终参数。

我知道的java集合中没有LIFO队列(如果错误,请纠正我),但您可以轻松地创建一个扩展LinkedBlockingQueue并覆盖相应方法的匿名内部类。

例如,(未经测试)

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);
  }

});

更新以回应评论。

我们可以使用包装优先级队列的阻塞队列。我们必须换行,因为Executor期望runnables,但我们也需要时间戳。

// 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<Runnable>() {

        private Comparator          comparator      = new Comparator<Pair>() {

                                                @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<Pair> backingQueue    = new PriorityBlockingQueue<Pair>(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
    });

答案 1 :(得分:9)

您可以通过两个或三个简单步骤完成:

  1. 创建LifoBlockingDeque类:

    public class LifoBlockingDeque <E> extends LinkedBlockingDeque<E> {
    
    @Override
    public boolean offer(E e) { 
        // Override to put objects at the front of the list
        return super.offerFirst(e);
    }
    
    @Override
    public boolean offer(E e,long timeout, TimeUnit unit) throws InterruptedException { 
        // Override to put objects at the front of the list
        return super.offerFirst(e,timeout, unit);
    }
    
    
    @Override
    public boolean add(E e) { 
        // Override to put objects at the front of the list
        return super.offerFirst(e);
    }
    
    @Override
    public void put(E e) throws InterruptedException { 
        //Override to put objects at the front of the list
        super.putFirst(e);
        }
    }
    
  2. 创建执行程序:

    mThreadPool = new ThreadPoolExecutor(THREAD_POOL_SIZE, 
                                         THREAD_POOL_SIZE, 0L, 
                                         TimeUnit.MILLISECONDS, 
                                         new LifoBlockingDeque<>());
    
  3. 仅从API级别9支持
  4. LinkedBlockingDeque。要在早期版本中使用它,请执行以下操作:

    使用Java 1.6实现 - 从here下载。

    然后改变

    implements BlockingDeque<E>
    

    implements BlockingQueue<E>
    

    使其在Android上编译。 BlockingDequeBlockingQueue的子类型,因此没有造成任何损害。

  5. 你已经完成了!

答案 2 :(得分:2)

ThreadPoolExecutorconstructor,允许指定要使用的队列类型。您可以在其中插入任何BlockingQueue,并且可能priority队列可能适合您。您可以将优先级队列配置为根据您添加到下载作业的(创建)时间戳进行排序,执行程序将按所需顺序执行作业。

答案 3 :(得分:1)

我有相同的要求:延迟加载和LIFO以获得更好的用户体验。所以我使用了ThreadPoolExecutor和一个包装的BlockingQueue(如前所述)。

为了便于向后兼容,我决定采用简单的方法,对于旧设备,我只是使用固定的线程池 - 这意味着FIFO排序。这不是完美的,但第一次尝试没问题。这看起来像:

        try {
            sWorkQueue = new BlockingLifoQueue<Runnable>();
            sExecutor = (ThreadPoolExecutor) Class.forName("java.util.concurrent.ThreadPoolExecutor").getConstructor(int.class, int.class, long.class, TimeUnit.class, BlockingQueue.class).newInstance(3, DEFAULT_POOL_SIZE, 10, TimeUnit.MINUTES, sWorkQueue);

            if (BuildConfig.DEBUG) Log.d(LOG_TAG, "Thread pool with LIFO working queue created");
        } catch (Exception e) {
            if (BuildConfig.DEBUG) Log.d(LOG_TAG, "LIFO working queues are not available. Using default fixed thread pool");

            sExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(DEFAULT_POOL_SIZE);
        }