«Один исполнитель LIFO» / SwingWorker - PullRequest
3 голосов
/ 13 ноября 2010

Рассмотрим приложение Swing с JList или JTable, когда при изменении выбора запускается SwingWorker, который загружает связанные данные из базы данных и обновляет пользовательский интерфейс. Это отлично работает, а пользовательский интерфейс отзывчивый.

Но если пользователь быстро меняет выбранную строку (удерживая клавишу вверх / вниз), я хочу быть уверен, что последняя выбранная строка - это та, которая загружена последней, а также я не хочу запрашивать БД в напрасно. Так что мне нужен однопоточный Executor с LIFO-очередью размером = 1. Таким образом, отправка ему задачи удаляет все предыдущие отправленные задачи и заставляет ее выполнять не более 1 задачи за раз, а не более 1 задачи ожидает ее выполнения.

Я не смог найти ничего подобного в java.util.concurrent, поэтому я написал своего собственного исполнителя. Я был прав в этом или я что-то упустил из параллельного пакета? Является ли решение приемлемым или есть лучшие способы достижения того, чего я хочу?

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

А вот пример, показывающий, как его можно использовать:

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

Ответы [ 3 ]

1 голос
/ 23 ноября 2010

LinkedBlockingDeque, похоже, все еще использует очереди с ThreadPoolExecutor.

Поэтому вместо этого я использовал обертку и использовал ее с ThreadPoolExecutor:

package util;

import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;

/**
 * LIFO BlockingQueue to be used with the ExecutorService.
 * @author Daniel
 * @param <T>
 */
public class LinkedBlockingStack<T> implements BlockingQueue<T>{
    private final LinkedBlockingDeque<T> stack = new LinkedBlockingDeque<T>();

    @Override
    public T remove() {
        return stack.remove();
    }

    @Override
    public T poll() {
        return stack.poll();
    }

    @Override
    public T element() {
        return stack.element();
    }

    @Override
    public T peek() {
        return stack.peek();
    }

    @Override
    public int size() {
        return stack.size();
    }

    @Override
    public boolean isEmpty() {
        return stack.isEmpty();
    }

    @Override
    public Iterator<T> iterator() {
        return stack.iterator();
    }

    @Override
    public Object[] toArray() {
        return stack.toArray();
    }

    @Override
    public <S> S[] toArray(final S[] a) {
        return stack.toArray(a);
    }

    @Override
    public boolean containsAll(final Collection<?> c) {
        return stack.containsAll(c);
    }

    @Override
    public boolean addAll(final Collection<? extends T> c) {
        return stack.addAll(c);
    }

    @Override
    public boolean removeAll(final Collection<?> c) {
        return stack.removeAll(c);
    }

    @Override
    public boolean retainAll(final Collection<?> c) {
        return stack.removeAll(c);
    }

    @Override
    public void clear() {
        stack.clear();
    }

    @Override
    public boolean add(final T e) {
        return stack.offerFirst(e); //Used offerFirst instead of add.
    }

    @Override
    public boolean offer(final T e) {
        return stack.offerFirst(e); //Used offerFirst instead of offer.
    }

    @Override
    public void put(final T e) throws InterruptedException {
        stack.put(e);
    }

    @Override
    public boolean offer(final T e, final long timeout, final TimeUnit unit)
    throws InterruptedException {
        return stack.offerLast(e, timeout, unit);
    }

    @Override
    public T take() throws InterruptedException {
        return stack.take();
    }

    @Override
    public T poll(final long timeout, final TimeUnit unit)
    throws InterruptedException {
        return stack.poll();
    }

    @Override
    public int remainingCapacity() {
        return stack.remainingCapacity();
    }

    @Override
    public boolean remove(final Object o) {
        return stack.remove(o);
    }

    @Override
    public boolean contains(final Object o) {
        return stack.contains(o);
    }

    @Override
    public int drainTo(final Collection<? super T> c) {
        return stack.drainTo(c);
    }

    @Override
    public int drainTo(final Collection<? super T> c, final int maxElements) {
        return stack.drainTo(c, maxElements);
    }
}
0 голосов
/ 18 октября 2011

Это решение, которое я реализовал, прекрасно работает для проблемы, которую я пытался решить:)

/**
 * 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);
    }
}
0 голосов
/ 22 ноября 2010

BlockingDeque Я считаю, что вы хотите. Поддерживает стеки.

Что у меня в коде:

private transient final ExecutorService threadPool= 
     new ThreadPoolExecutor(3, 10,10, 
                            TimeUnit.MILLISECONDS, 
                            new LinkedBlockingDeque<Runnable>());
...