【问题标题】:How to make sure the done() method of two concurrent swingworkers is executed in EDT in the right order?如何确保两个并发 swingworker 的 done() 方法在 EDT 中以正确的顺序执行?
【发布时间】:2016-10-05 16:12:33
【问题描述】:

您好,我一直在尝试学习如何使用 SwingWorker 来更新 Swing 应用程序的 GUI。

我的问题是:如果我有 2 个可以同时运行的 swingworker(每个都从不同的按钮触发)如何确保在 EDT 中以相同的顺序调用他们的 done 方法(以更新 gui)他们被解雇了?

我的观察和想法: 我试图将他们的 doInBackground() 方法与锁同步,以确保一个必须等​​待另一个完成任务才能继续。问题是我已经能够(仅几次)看到 swingworker_1 完成 doInBackground() (而第二个工人正在同步块中等待),但是 Swingworker_2 的 done 方法能够被 EDT 调用在 swingworker_1 的 done 方法之前,即使 swingworker_2 之后完成。我考虑在 doInBackground() 返回之前使用发布,这似乎总是有效,但可能是一种不好的做法,因为它似乎用于中间结果而不是最终结果?另一种可能性是在 doInBackground() 中使用 SwingUtilities.invokeLater 在返回之前更新 gui,我担心这也可能是一个不好的做法。提前致谢。

我使用的测试,按下第一个按钮,然后立即按下第二个按钮:

    //Code that runs in the first button
    SwingWorker<Void, Void> swingworker = new SwingWorker<Void, Void>() {

        @Override
        protected Void doInBackground() throws Exception {
            synchronized (lock) {
                System.out.println("Thread 1");

                Thread.sleep(1000);
                return null;
            }

        }

        @Override
        protected void done() {

            System.out.println("Done with thread 1 ");
            //update GUI    
        }

    };
    swingworker.execute();



    //Code that runs in the second button
    SwingWorker<Void, Void> swingworker = new SwingWorker<Void, Void>() {

        @Override
        protected Void doInBackground() throws Exception {
            synchronized (lock) {
                System.out.println("Thread 2");

                return null;
            }

        }

        @Override
        protected void done() {
            System.out.println("Done with thread 2 ");
            //update GUI
        }

    };
    swingworker.execute();

更新:只是为了澄清我不关心谁先开始(swingworker_1 或 swingworker_2),这取决于首先按下哪个按钮。我要保证的是,如果来自按钮的工作人员是第一个执行的(并因此在工作人员在 doInBackground() 方法中同步时完成),那么它也应该是第一个加入队列以更新 EDT 中的 gui .我发现即使工作人员被同步,这也不一定会发生,有时稍后启动任务的工作人员仍然设法在第一个之前更新 gui。

【问题讨论】:

  • 我想知道您是否在描述一个 XY 问题,并要求您提供更多信息:首先,为什么这个顺序如此重要?这些工人在做什么,你的项目在做什么?
  • 我自己,我尽量避免使用done() 方法,而是通常使用PropertyChangeListeners 监听worker 的状态变化。这减轻了 SwingWorker 必须进行任何特定于 GUI 的调用的责任,甚至可以了解 GUI 或任何调用代码。
  • 我在您的代码中没有看到任何东西可以确保第一个工作人员在第二个工作人员之前获得lock 的监视器。
  • 你好气垫船,想法是使用第一个 swingworker/按钮来启动后台数据,完成后它会将标签或其他内容更新为“在线”。第二个按钮可用于关闭,它将负责终止所有后台线程,完成后它将标签更新为离线。
  • 你好 John,我们的想法不是确保一个在另一个之前运行,而是当一个 swingworker 运行时它会在另一个之前更新 gui,所以如果在第二个按钮被按下之前它应该更新gui 在按钮 1 之前,反之亦然。

标签: java swing concurrency swingworker


【解决方案1】:

如果您对连续运行的工作人员感到满意,如果您根据示例代码在锁上同步,他们会这样做,并且您没有使用doInBackground()done() 以外的任何其他SwingWorker 功能,您可能更容易使用单线程执行器和SwingUtilities.invokeLater(...) 在 GUI 上呈现结果。

这样,您可以保证提交给执行器的事物将按照它们提交的顺序运行。

如果这对你有用,你可以试试这样:

final ExecutorService executor = Executors.newSingleThreadExecutor();

// Code that runs in the first button
Runnable worker1 = new Runnable() {
  @Override
  public void run() {
    System.out.println("Thread 1");
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace(System.err);
    }
    SwingUtilities.invokeLater(this::updateGUI);
  }

  private void updateGUI() {
    System.out.println("Done with thread 1");
    //update GUI
  }
};
executor.execute(worker1);

// Code that runs in the second button
Runnable worker2 = new Runnable() {
  @Override
  public void run() {
    System.out.println("Thread 2");
    SwingUtilities.invokeLater(this::updateGUI);
  }

  private void updateGUI() {
    System.out.println("Done with thread 2");
    //update GUI
  }
};
executor.execute(worker2);

【讨论】:

  • 你好 msandiford,这肯定会像你说的那样工作,工作人员一个接一个地执行,他们在返回之前排队更新 gui。为了适应这一点,我将在 button1 中执行 worker1,在 button2 中执行 worker2。正如我最初在我的问题中所说的那样,我确信在 swingworkers 的 doInBackground() 中使用invokelater 也可以,我认为发布也可以做到这一点,只是它们可能被认为是不好的做法,每个都有不同的原因?完成的工作不必按照工人完成的顺序排队,这似乎是一个奇怪的设计。
  • 您可以在doInBackground 中使用synchronized 块,并在同步块内调用invokeLater,但最终SwingWorker 使用多线程执行器,因此没有真正的保证对于几乎同时提交的 SwingWorkers,哪个 worker 将首先运行。鉴于工作人员提交是基于单击 GUI 按钮,这对您的用例可能并不重要。
  • 正是msandiford,反正谁先运行并不重要,只要先运行的那个将首先排队等待对gui的更新。似乎稍后调用,无论是您的方法(顺便说一句,这是一个很好且聪明的方法)还是在 swingworker 同步的 doInBackground() 内部,都将确保满足此顺序。另一方面, done 方法不能保证这一点。之前建议的 propertyChangeListener 也可能是一个优雅的解决方案,但我担心它也可能无法保证正确的顺序,因为完成 doInBackground() 和更改为完成状态之间可能会有延迟。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-21
  • 1970-01-01
  • 2021-07-23
  • 1970-01-01
  • 2019-05-24
  • 2020-02-27
  • 2018-07-30
相关资源
最近更新 更多