Исполнительная служба с заказом LIFO

Я написал ленивый загрузчик изображений для своего приложения, используя ExecutorService. Это дает мне большой контроль над тем, сколько загрузок выполняется параллельно в какое время и т.д.

Теперь единственная проблема, с которой я столкнулась, заключается в том, что если я отправлю задачу, она заканчивается в хвосте очереди (FIFO).

Кто-нибудь знает, как изменить это на LIFO?

Ответ 1

Вам нужно будет указать тип очереди, который использует ExecutorService.

Обычно вы можете получить ExecutorService через статические методы в Executors. Вместо этого вам нужно будет создать экземпляр напрямую и передать тип очереди, который вы хотите предоставить LIFO.

EG, чтобы создать исполнитель пула потоков LIFO, вы можете использовать следующий конструктор.

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

и передать в очередь LIFO в качестве конечного параметра.

В коллекциях java нет ни одной очереди LIFO (пожалуйста, исправьте меня, если не так), но вы можете просто создать анонимный внутренний класс, который расширяет LinkedBlockingQueue и отменяет соответствующие методы.

Например, (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 в ответ на комментарии.

Мы можем использовать блокирующую очередь, которая обертывает очередь приоритетов. Мы должны обернуть, потому что 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
    });

Ответ 2

Вы можете сделать это в два или три простых шага:

  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<Runnable>());
    
  3. LinkedBlockingDeque поддерживается только с уровня API 9. Чтобы использовать его в более ранних версиях, выполните следующие действия:

    Используйте реализацию Java 1.6 - загрузите ее с здесь.

    Затем измените

    implements BlockingDeque<E>
    

    в

    implements BlockingQueue<E>
    

    Чтобы сделать его на Android. BlockingDeque является подтипом BlockingQueue, поэтому никакого вреда не будет.

И вы сделали!

Ответ 3

ThreadPoolExecutor имеет конструктор который позволяет указать тип очереди для использования. Вы можете подключить к нему BlockingQueue, и, возможно, ваша очередь может оказаться priority. Вы можете настроить очередь приоритетов для сортировки на основе метки времени создания (создания), которую вы добавляете к заданиям загрузки, и исполнитель выполнит задания в нужном порядке.

Ответ 4

У меня были те же требования: Lazy loading и 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);
        }