【问题标题】:SwingWorker cancellation with ThreadPoolExecutor使用 ThreadPoolExecutor 取消 SwingWorker
【发布时间】:2010-11-03 17:48:44
【问题描述】:

我正在使用线程池大小为 1 的 ThreadPoolExecutor 来顺序执行摇摆工作人员。我遇到了一个特殊情况,即一个事件到达,它创建了一个执行一些客户端-服务器通信的 swing worker,然后更新 ui(在 done() 方法中)。

当用户触发(点击某个项目)某些事件时,这可以正常工作,但如果发生许多事件则不会。但这发生了,所以我需要取消所有当前正在运行和计划的工作人员。问题是支持 ThreadPoolExecutor 的队列不知道 SwingWorker 取消过程(至少看起来是这样)。所以预定的工人会被取消,但已经在运行的工人不会。

所以我添加了一个<T extends SwingWorker> 类型的并发队列,它保存所有工作人员的引用,只要它们没有被取消,并且当新事件到达时,它会在队列中的所有 SwingWorkers 上调用 .cancel(true) 并提交新的 SwingWorker 到 ThreadPoolExecutor。

总结:SwingWorkers 是在具有单个线程的 ThreadPoolExecutor 中创建和执行的。只有最后提交的工作人员才应该运行。

有没有其他方法可以解决这个问题,或者这样做“可以”吗?

只是好奇……

【问题讨论】:

  • 有效吗?如果它没有坏,请不要修复它(或者修复它直到它坏了,哈哈)
  • 是的,它有效。但是编写并发代码很难。我只是想知道我的任务是否存在任何概念。

标签: java multithreading swing threadpool


【解决方案1】:

创建只执行最后一个传入的 Runnable 的单线程 ThreadPoolExecutor 的一种方法是子类化一个合适的队列类并覆盖所有添加方法以在添加新的 runnable 之前清除队列。然后将该队列设置为 ThreadPoolExecutor 的工作队列。

【讨论】:

  • 不错。这比我保留一个单独的需要取消的任务列表的想法要干净得多。
  • 这很像我自己的实现。
【解决方案2】:

让我看看我是否正确理解了这个问题。您有一个 FIFO 任务队列,其中只有最旧的任务正在运行。每个任务完成后都需要更新 UI。但是如果某个用户事件进来了,所有的任务都需要被取消——也就是正在运行的任务需要被取消,尚未运行的任务需要从队列中移除。对吗?

假设是这样,我不会使用SwingWorker,因为您只需要一个工作线程,而不是每个任务一个。 FutureTask 应该足够了(假设您覆盖 done() 以对 SwingUtilities.invokeLater() 进行必要的调用并进行 UI 更新)。

如果你取消一个FutureTask,那么即使它的run() 方法被调用,它也不会做任何事情。因此,您可以安全地将FutureTasks 提交给ExecutorService,因为即使执行程序尝试运行它们,取消也会起作用。

我怀疑一个足够好的解决方案只是保留所有可能需要取消的FutureTasks 的列表,并在用户事件出现时将它们全部取消。ExecutorService 仍将尝试运行他们,但它基本上是一个无操作。您需要确保从列表中删除已完成的任务,并且您需要确保以线程安全的方式更新和使用列表(可能来自将任务放在ExecutorService 上的同一线程),但这应该不要太难。

我只用了一个小时就把下面的代码搞砸了,我不敢打赌它是正确的,但你明白了。 :)

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

反正就是这样。

如果您想更加确定,则可以关闭并替换ExecutorService,但这可能没有必要。

【讨论】:

    【解决方案3】:

    除了使用 SwingWorker 之外,您是否可以不使用 ThreadPoolExecutor 来执行客户端-服务器通信,然后调用 SwingUtilities.invokeLater 来使用结果更新 UI?这对我来说似乎更干净一些,并且可以确保事件和 UI 更新仍然按顺序处理。

    当您向执行者提交任务时,您可以保留对其 Future 实例的引用,以便在需要时取消任务。

    【讨论】:

    • 问题是我可以从 executor 那里得到的 future 并没有像 swing worker 那样提供取消的可能性,因为它只是包装了可运行的接口。
    • 但是它们都提供了相同的取消方法。 SwingWorker 实现了 Future 接口。
    • 就像我说的。执行者似乎没有意识到这一点。并将其作为 Runnable 执行
    【解决方案4】:

    为什么需要 ThreadPoolExecutor 来完成这种工作?

    您有多少不同 SwingWorker 的来源?因为如果源只是一个,您应该使用不同的方法。

    例如,您可以定义一个处理一种工作线程的类,并将其链接到一种项目,用户可以在该项目上触发操作并在该类内部关心该线程的单个实例应该运行的事实(例如使用完成任务后清除的单例实例)

    【讨论】:

    • 我需要一个带有单线程的线程池执行器,以确保一次只运行一个任务。但是其中很多都不会发生,所以我必须排队。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-09
    • 2011-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-11
    相关资源
    最近更新 更多