Исполнитель Сервис с заказом LIFO - PullRequest
8 голосов
/ 24 мая 2011

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

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

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

Ответы [ 4 ]

9 голосов
/ 24 мая 2011

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

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

});

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
    });
9 голосов
/ 25 ноября 2011

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

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

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

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

    implements BlockingDeque<E>
    

    до

    implements BlockingQueue<E>
    

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

И все готово!

2 голосов
/ 24 мая 2011

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

1 голос
/ 24 июля 2012

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