【问题标题】:SwingWorker: when exactly is called done method?SwingWorker:何时调用 done 方法?
【发布时间】:2011-09-06 10:38:47
【问题描述】:

SwingWorker:的done()方法的Javadoc

在 doInBackground 方法之后在 Event Dispatch Thread 上执行 结束了。

我有线索表明,在取消工作人员的情况下,情况并非如此。
Done 在每种情况下都会被调用(正常终止或取消),但是当cancelled 它未入队 到 EDT,就像正常终止一样。

在取消SwingWorker 的情况下,何时调用done 是否有更精确的分析?

澄清: 这个问题不是关于如何cancel 和SwingWorker。这里假设SwingWorker以正确的方式取消。
这不是关于线程在应该完成时仍在工作的情况。

【问题讨论】:

  • 为什么不看源码?
  • 这很奇怪。我有 Java 6,文档说 SwingWorker 包含在 Java 6 中,但我在 rt.jar 中找不到它! :-/
  • 啊,它在 src.zip 但没有 .class 文件。太好了……
  • @mKorblel:我已经非常仔细地阅读了关于 SwingWorker 的教程,也阅读了 java 文档。如果你也有,你就会知道本教程只是展示了一个基本的取消示例,而不是详细说明。
  • @mKorblel:对不起,mKorblel,你仔细阅读我的问题了吗?

标签: java swing swingworker cancellation


【解决方案1】:

当一个线程被取消时

myWorkerThread.cancel(true/false);

done 方法(非常令人惊讶)由 cancel 方法本身调用。

您可能期望发生的事情,但实际上并没有发生:
- 您调用取消(使用 mayInterrupt 或不使用)
- cancel 设置线程取消
- doInBackground 退出
- 完成被称为*
(* done 被 enqueued 到 EDT,这意味着,如果 EDT 很忙,它会在 EDT 完成它正在做的事情之后发生)

实际发生了什么:
- 您调用取消(使用 mayInterrupt 或不使用)
- cancel 设置线程取消
- 完成被称为取消代码的一部分*
- doInBackground 将在完成循环后退出
(*done 没有加入 EDT,而是调用到取消调用中,因此它对 EDT 有非常直接的影响,通常是 GUI)

我提供了一个简单的例子来证明这一点。
复制、粘贴和运行。
1.我在done里面生成了一个运行时异常。堆栈线程显示完成是由取消调用的。
2. 取消后大约 4 秒后,你会收到来自 doInBackground 的问候,这进一步证明在线程退出之前调用了 done。

import java.awt.EventQueue;
import javax.swing.SwingWorker;

public class SwingWorker05 {
public static void main(String [] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
            W w = new W();
            w.execute();
            Thread.sleep(1000);
            try{w.cancel(false);}catch (RuntimeException rte) {
                rte.printStackTrace();
            }
            Thread.sleep(6000);
            } catch (InterruptedException ignored_in_testing) {}
        }

    });
}

public static class W extends SwingWorker <Void, Void> {

    @Override
    protected Void doInBackground() throws Exception {
        while (!isCancelled()) {
            Thread.sleep(5000);
        }
        System.out.println("I'm still alive");
        return null;
    }

    @Override
    protected void done() {throw new RuntimeException("I want to produce a stack trace!");}

}

}

【讨论】:

  • +1 与我在同一时间得到的结果相同(令人惊讶);-)
  • 因此,您甚至可以在done 之后发布/处理结果。
  • 这已被 Oracle 接受为错误:bugs.sun.com/bugdatabase/view_bug.do?bug_id=6826514
  • 该错误报告尚未更新,但在 Java 7 中调用 #cancel() 不会调用 #done() (Oracle JDK)
  • 抱歉,@searchengine27,但事实并非如此。现在查看 Java 8 JRE 源代码; SwingWorker#cancel() 调用 FutureTask#cancel(),如果它不立即返回 false,则调用私有辅助方法 finishCompletion(),该方法无条件调用 FutureTask#done()。 SwingWorker 的 FutureTask 的匿名子类覆盖 done() 以调用自己的私有 SwingWorker#doneEDT(),后者在 EDT 上无条件地调用或排队自己的 done()。
【解决方案2】:

done() 在任何情况下都会被调用,无论 worker 被取消还是正常结束。尽管如此,在某些情况下,doInBackground 仍在运行并且已经调用了 done 方法(无论线程是否已经完成,这都是在 cancel() 内部完成的)。一个简单的例子可以在这里找到:

public static void main(String[] args) throws AWTException {
    SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>() {

        protected Void doInBackground() throws Exception {
            System.out.println("start");
            Thread.sleep(2000);
            System.out.println("end");
            return null;
        }

        protected void done() {
            System.out.println("done " + isCancelled());
        }
    };
    sw.execute();
    try {
        Thread.sleep(1000);
        sw.cancel(false);
        Thread.sleep(10000);
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }

因此,done 可能在 doInBackground 完成之前被调用。

【讨论】:

  • 好的。代码与我提供的几乎相同。此外,我证明 done 发生在使用堆栈跟踪技巧的取消内部。
【解决方案3】:

某事是可能的,其他的可能是幻觉

真的很不错

run:
***removed***
java.lang.RuntimeException: I want to produce a stack trace!
        at help.SwingWorker05$W.done(SwingWorker05.java:71)
        at javax.swing.SwingWorker$5.run(SwingWorker.java:717)
        at javax.swing.SwingWorker.doneEDT(SwingWorker.java:721)
        at javax.swing.SwingWorker.access$100(SwingWorker.java:207)
        at javax.swing.SwingWorker$2.done(SwingWorker.java:284)
        at java.util.concurrent.FutureTask$Sync.innerCancel(FutureTask.java:293)
        at java.util.concurrent.FutureTask.cancel(FutureTask.java:76)
        at javax.swing.SwingWorker.cancel(SwingWorker.java:526)
        at help.SwingWorker05$1.run(SwingWorker05.java:25)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
I'm still alive
Thread Status with Name :SwingWorker1, SwingWorker Status is STARTED
SwingWorker by tutorial's background process has completed
Thread Status with Name :SwingWorker1, SwingWorker Status is DONE
Thread Status with Name :look here what's possible with SwingWorker, SwingWorker Status is STARTED
BUILD SUCCESSFUL (total time: 10 seconds)

来自

import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.SwingWorker;

public class SwingWorker05 {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            public void run() {
                try {
                    W w = new W();
                    w.addPropertyChangeListener(
                            new SwingWorkerCompletionWaiter("look here what's possible with SwingWorker"));
                    w.execute();
                    Thread.sleep(1000);
                    try {
                        w.cancel(false);
                    } catch (RuntimeException rte) {
                        rte.printStackTrace();
                    }
                    Thread.sleep(6000);
                } catch (InterruptedException ignored_in_testing) {
                }
            }
        });

        final MySwingWorker mySW = new MySwingWorker();
        mySW.addPropertyChangeListener(new SwingWorkerCompletionWaiter("SwingWorker1"));
        mySW.execute();
    }

    private static class MySwingWorker extends SwingWorker<Void, Void> {

        private static final long SLEEP_TIME = 250;

        @Override
        protected Void doInBackground() throws Exception {
            Thread.sleep(SLEEP_TIME);
            return null;
        }

        @Override
        protected void done() {
            System.out.println("SwingWorker by tutorial's background process has completed");
        }
    }

    public static class W extends SwingWorker {

        @Override
        protected Object doInBackground() throws Exception {
            while (!isCancelled()) {
                Thread.sleep(5000);
            }

            System.out.println("I'm still alive");
            return null;
        }

        @Override
        protected void done() {
            System.out.println("***remove***");
            throw new RuntimeException("I want to produce a stack trace!");
        }
    }

    private static class SwingWorkerCompletionWaiter implements PropertyChangeListener {

        private String str;

        SwingWorkerCompletionWaiter(String str) {
            this.str = str;
        }

        @Override
        public void propertyChange(PropertyChangeEvent event) {
            if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.DONE == event.getNewValue()) {
                System.out.println("Thread Status with Name :" + str + ", SwingWorker Status is " + event.getNewValue());
            } else if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.PENDING == event.getNewValue()) {
                System.out.println("Thread Status with Mame :" + str + ", SwingWorker Status is " + event.getNewValue());
            } else if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.STARTED == event.getNewValue()) {
                System.out.println("Thread Status with Name :" + str + ", SwingWorker Status is " + event.getNewValue());
            } else {
                System.out.println("Thread Status with Name :" + str + ", Something wrong happends ");
            }
        }
    }
}

【讨论】:

  • @mKorbel。我正在处理您的示例。第一个问题。由于我对 SwingWorker 方法的实现是 6 行,您能指出“错误的实现”在哪里吗?第二。我从未使用过“错误”一词。如果您阅读了这个问题,您会看到这是关于何时调用取消的。毫无疑问,我已经证明,是的,在每种情况下都会调用 done(在 EDT 中,正如预期的那样!)并且在取消的情况下,它不会排入 EDT,而是作为 cancel(...) 调用的一部分调用,可能在退出 doInBackground 方法之前。现在我正在验证 PropertyChangeListener 的确切行为,我会告诉你的。跨度>
  • @AgostinoX :-) 因为我对 SwingWorker 方法的实现是 6 行 :-) 不,这不仅仅是普通的 innerClass 或 voids 1st。等待计时器,第二个。对于第一端,没什么,小错误...,让我们回到开始(Coldplay),最重要的是,如果您想玩 SwingWorker,那么您的代码可以直接确认其输出到 GUI(JTextArea),否则不会'没有意义,因为 System.out.print() 在所有情况下都有效(+ - CitiBus),非常幸运
  • 好的。所以 W 类是对的。你介意删除关于“错误实现类”的评论吗?谢谢。然后你说问题是在等待计时器?哪个定时器?也许你的意思是 Thread.sleep。我做了一个类,每个人都可以用来重现我正在谈论的特定问题,所以我需要一些“挂起”几秒钟的东西,比如慢速数据库连接。我确信它可以以更好的方式完成,所以我很高兴收到您的来信。
  • @@AgostinoX 确定我会/只能根据您的要求将其删除,但正确的语法不会改变必须包括 extends SwingWorker//...,否则不是我们所知道的 SwingWorker,只有一些需要一些方法但没有声明 SWingWorkers 功能的类...,所以现在我终于离开了这个线程
  • @mKorbel.I,也应您的要求,将 添加到我的 SwingWorker 实现中,因为它可以帮助过去看到泛型的人们更好地理解该类。但我想指出,功能不是由泛型提供的,而是通过扩展类或实现接口来提供的。泛型是一种避免类型转换的方法。而且,由于它们是通过擦除实现的,所以编译后的代码有无都是一样的;我们谈论的是便利和习惯,而不是正确性。
【解决方案4】:

直到 SwingWorker 修复 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6826514 这里是一个简单(测试)版本,具有基本(类似)功能,然后是 SwingWorker

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tools;

import java.util.LinkedList;
import java.util.List;
import javax.swing.SwingUtilities;

/**
 *
 * @author patrick
 */
public abstract class MySwingWorker<R,P> {

    protected abstract R doInBackground() throws Exception;
    protected abstract void done(R rvalue, Exception ex, boolean canceled);
    protected void process(List<P> chunks){}
    protected void progress(int progress){}

    private boolean cancelled=false;
    private boolean done=false;
    private boolean started=false;
    final private Object syncprogress=new Object();
    boolean progressstate=false;
    private int progress=0;
    final private Object syncprocess=new Object();
    boolean processstate=false;
    private LinkedList<P> chunkes= new LinkedList<>();

    private Thread t= new Thread(new Runnable() {
        @Override
        public void run() {
            Exception exception=null;
            R rvalue=null;
            try {
                rvalue=doInBackground();
            } catch (Exception ex) {
                exception=ex;
            }

            //Done:
            synchronized(MySwingWorker.this)
            {
                done=true;
                final Exception cexception=exception;
                final R crvalue=rvalue;
                final boolean ccancelled=cancelled;

                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        done(crvalue, cexception, ccancelled);
                    }
                });
            }

        }
    });    

    protected final void publish(P p)
    {
        if(!Thread.currentThread().equals(t))
            throw new UnsupportedOperationException("Must be called from worker Thread!");
        synchronized(syncprocess)
        {
            chunkes.add(p);
            if(!processstate)
            {
                processstate=true;
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        List<P> list;
                        synchronized(syncprocess)
                        {
                            MySwingWorker.this.processstate=false;
                            list=MySwingWorker.this.chunkes;
                            MySwingWorker.this.chunkes= new LinkedList<>();
                        }
                        process(list);
                    }
                });
            }
        }
    }

    protected final void setProgress(int progress)
    {
        if(!Thread.currentThread().equals(t))
            throw new UnsupportedOperationException("Must be called from worker Thread!");
        synchronized(syncprogress)
        {
            this.progress=progress;
            if(!progressstate)
            {
                progressstate=true;
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        int value;
                        //Acess Value
                        synchronized(syncprogress)
                        {
                            MySwingWorker.this.progressstate=false;
                            value=MySwingWorker.this.progress;
                        }
                        progress(value);
                    }
                });
            }
        }
    }

    public final synchronized void execute()
    {
        if(!started)
        {
            started=true;
            t.start();
        }
    }

    public final synchronized boolean isRunning()
    {
        return started && !done;
    }

    public final synchronized boolean isDone()
    {
        return done;
    }

    public final synchronized boolean isCancelled()
    {
        return cancelled;
    }

    public final synchronized void cancel()
    {
        if(started && !cancelled && !done)
        {
            cancelled=true;
            if(!Thread.currentThread().equals(t))
                t.interrupt();
        }
    }

}

【讨论】:

    【解决方案5】:

    来自 Java 文档: 取消(布尔值可能InterruptIfRunning) "mayInterruptIfRunning - 如果执行此任务的线程应该被中断,则为 true;否则,允许完成正在进行的任务"

    如果您调用 cancel(true) 而不是 cancel(false),它的行为似乎符合您的预期。

    我没有看到 done() 使用 EventQueue.isDispatchThread() 取消 EDT

    【讨论】:

      【解决方案6】:

      如果你使用 return Void: ...@Override public Void doInBackground(){...

      doInBackground() 完成时调用done()。

      如果你不使用 return Void: ...@Override public boolean doInBackground(){...

      done() 被忽略,你知道已经完成,因为你有你的回报。

      【讨论】:

      • 欢迎来到 SO。这篇文章不符合我们的质量标准,如何写出高质量的答案阅读here。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-21
      • 2015-02-07
      • 1970-01-01
      • 2014-09-17
      • 1970-01-01
      相关资源
      最近更新 更多