Вопрос

Я использую ThreadPoolExecutor с размером пула потоков, равным единице, для последовательного выполнения рабочих процессов Swing. У меня есть особый случай, когда приходит событие, которое создает рабочий свинг, который выполняет некоторое взаимодействие клиент-сервер и после этого обновляет пользовательский интерфейс (в методе done ()).

Это прекрасно работает, когда пользователь запускает (нажимает на элемент) некоторые события, но не в том случае, если их много. Но это происходит, поэтому мне нужно отменить всех работающих в настоящее время и запланированных работников. Проблема в том, что очередь, которая поддерживает ThreadPoolExecutor, не знает о процессе отмены SwingWorker (по крайней мере, так). Так что наемный работник отменяется, а уже работающие - нет.

Поэтому я добавил параллельную очередь типа <T extends SwingWorker>, которая содержит ссылку на всех работников, если они не отменены, и когда приходит новое событие, он вызывает .cancel (true) для всех SwingWorkers в очереди и отправляет новый SwingWorker для ThreadPoolExecutor.

Резюме: SwingWorkers создаются и выполняются в ThreadPoolExecutor с одним потоком. Только тот работник, который был представлен последним, должен быть запущен.

Есть ли альтернативы для решения этой проблемы или " ok " сделать это так?

Просто любопытно ...

Это было полезно?

Решение

Одним из способов создания однопотокового ThreadPoolExecutor, который выполняет только последний входящий Runnable, является создание подкласса подходящего класса очереди и переопределение всех методов добавления, чтобы очистить очередь перед добавлением нового runnable. Затем установите эту очередь в качестве рабочей очереди ThreadPoolExecutor.

Другие советы

Зачем вам нужен ThreadPoolExecutor для выполнения такой работы?

Сколько источников разных SwingWorkers у вас есть? Потому что, если источник только один, вы должны использовать другой подход.

Например, вы можете определить класс, который обрабатывает один вид рабочего потока, и он связан с единственным видом элемента, над которым пользователь может запускать действия и заботиться внутри этого класса о том, что должен быть запущен один экземпляр потока. (например, использование одноэлементного экземпляра, который очищается при завершении задачи)

Вместо использования SwingWorker, не могли бы вы использовать ThreadPoolExecutor для выполнения взаимодействия клиент-сервер, а затем вызвать SwingUtilities.invokeLater для обновления пользовательского интерфейса с результатом? Это мне кажется немного чище и гарантирует, что события и обновления пользовательского интерфейса будут по-прежнему обрабатываться по порядку.

Когда вы отправляете задачи своему исполнителю, вы можете сохранить ссылку на их будущие экземпляры, чтобы при необходимости можно было отменить задачу.

Дайте мне посмотреть, правильно ли я понимаю проблему. У вас есть очередь задач FIFO, только самая старая из которых выполняется. Каждое задание должно обновлять интерфейс, когда оно выполнено. Но если наступает определенное пользовательское событие, все задачи должны быть отменены, то есть, должны быть отменены выполняемые задачи, а еще не выполненные задачи должны быть удалены из очереди. Это правильно?

Если это так, я бы не стал использовать SwingWorker, поскольку вам нужен только один рабочий поток, а не один на задачу. FutureTask должно быть достаточно (при условии, что вы переопределите done(), чтобы сделать необходимый вызов SwingUtilities.invokeLater() и выполнить обновление интерфейса).

Если вы отмените run(), то даже если будет вызван его метод ExecutorService, он ничего не сделает. Таким образом, вы можете безопасно отправлять FutureTasks сообщения <=>, зная, что отмена будет работать, даже если исполнитель попытается их запустить.

Я подозреваю, что достаточно хорошим решением было бы просто сохранить список всех <=>, которые, возможно, потребуется отменить, и отменить их все, когда наступит пользовательское событие. <=> все равно попытается запустить их, но это будет в основном неоперативным. Вы должны убедиться, что завершенные задачи удалены из списка, и вы должны убедиться, что список обновлен и используется потокобезопасным способом (вероятно, из того же потока, который помещает задачи в <=>), но это не должно не будет слишком сложно.

Я расшифровал приведенный ниже код всего за час, и я бы не стал делать ставку на то, что он правильный, но вы поняли идею. :)

/** Untested code! Use at own risk. */
public class SwingTaskExecutor {

    // ////////////////////////////////////////////////////////////
    // Fields

    private final ExecutorService execSvc = Executors.newFixedThreadPool(1);

    private final Lock listLock = new ReentrantLock();
    private final List<ManagedSwingTask<?>> activeTasks = 
            new ArrayList<ManagedSwingTask<?>>();

    // ////////////////////////////////////////////////////////////
    // Public methods

    public <T> Future<T> submit(SwingTask<T> task) {
        ManagedSwingTask<T> managedTask = new ManagedSwingTask<T>(task);
        addToActiveTasks(managedTask);
        execSvc.submit(managedTask);
        return managedTask;
    }

    public void cancelAllTasks() {
        listLock.lock();
        try {
            for (ManagedSwingTask<?> t: activeTasks) {
                t.cancel(true);
            }
            activeTasks.clear();
        } finally {
            listLock.unlock();
        }
    }

    // ////////////////////////////////////////////////////////////
    // Private methods

    private <T> void addToActiveTasks(ManagedSwingTask<T> managedTask) {
        listLock.lock();
        try {
            activeTasks.add(managedTask);
        } finally {
            listLock.unlock();
        }
    }

    // ////////////////////////////////////////////////////////////
    // Helper classes

    private class ManagedSwingTask<T> extends FutureTask<T> {

        private final SwingTask<T> task;

        ManagedSwingTask(SwingTask<T> task) {
            super(task);
            this.task = task;
        }

        @Override
        public void cancel(boolean mayInterruptIfRunning) {
            try {
                task.cancel();
            } finally {
                super.cancel(mayInterruptIfRunning);
            }
        }

        @Override
        protected void done() {
            removeFromActiveTasks();
            updateUIIfDone();
        }

        private void removeFromActiveTasks() {
            listLock.lock();
            try {
                activeTasks.remove(this);
            } finally {
                listLock.unlock();
            }
        }

        private void updateUIIfDone() {
            if (isDone()) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        task.updateUI();
                    }
                });
            }
        }
    }

    public static interface SwingTask<T> extends Callable<T> {

        /** Called from the EDT if task completes successfully */
        void updateUI();

        /** Hook in case there's task-specific cancellation to be done*/
        void cancel();
    }
}

Во всяком случае, что-то подобное.

Если вы хотите быть уверенным вдвойне, вы можете закрыть и заменить <=>, но это, вероятно, не обязательно.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top