使用SwingWorker

时间:2016-02-22 19:52:59

标签: java multithreading swing unit-testing swingworker

注意这不是终止工作线程。这是关于正常终止的后果。

我在EDT中呼叫execute,因此产生了“工作线程” 我在worker.get()中拨打Callable,我提交给ExecutorService,特别是ThreadPoolExecutor

任务结束OK(即worker.get()返回结果,Callable也结束OK)。

我在shutdown()上调用Executor Service:方法正常终止。

当我在此之后检查线程时,就会出现这个难题。这是在单元测试的背景下,我基本上想要杀掉上次测试期间开始的所有活动。

我列出了所有线程,除了在测试开始之前出现的各种线程,我看到了这一点:

  

#thread线程[pool-2-thread-1,5,main],状态WAITING,alive   True,中断False,线程组主要#thread   线程[SwingWorker-pool-4-thread-1,5,main],state WAITING,alive True,   中断False,主题组主要

问题是,随着测试的运行,新构造的“Application”对象每次都会创建一个新的线程池,并且越来越多的SwingWorkers被创建......并且这些线程似乎都没有转换到Thread.State“终止”(这可能意味着他们将不再列出)。

然后我尝试在“SwingWorker-pool-4-thread”join上调用Thread ...但这会导致我当前的线程挂起。

有人可以解释这里发生了什么以及如何将这些线程转换为“TERMINATED”吗?

回复2条有用的评论。 10个线程,对。我的想法只是测试它是否失败(或发生异常)这会使事情处于一种麻烦状态:假设,在功能测试中,涉及3个SW,并且各种publish/process内容正在进行中,更不用说美国东部时间的各种Runnable。在进行下一次测试之前,我想让一切都关闭,关闭,结束,杀死,回到原始的处女状态!

为确保所有Runnable已结束,我们有Robot.waitForIdle()。我还想到了确保publish/process事情已经结束的策略:SwingWorker, done() is executed before process() calls are finished ....但事实上,后者虽然很有用,但预先假定测试代码知道哪些SW调用该方法......我正在寻找一个大错,它只会杀死死神。

这些Thread对象的toString清楚地显示了自开始最新测试以来创建的内容......但我不知道如何杀死它们。是不是“等待”意味着,吸血鬼一样,他们可能会再起来并做各种各样的坏事?只是留下大量“等待”的线程,可能会遇到数百个,看起来有点......不整洁!

甚至更晚

啊,是的,抓住AppContext对象至关重要,因为你需要“重置”这个以防止RejectedExecutionException。请参阅我在Jython中如何做的答案。

我还注意到,在SW API文档中它说

  

因为SwingWorker实现了Runnable,所以可以使用SwingWorker   提交给执行人执行。

这可能意味着您根本不必使用SW的默认执行程序。可能是要走的路,但除非在应用程序代码中出于其他原因需要,否则为了方便测试代码,它违反了不定制应用程序代码的原则......

2 个答案:

答案 0 :(得分:5)

免责声明:我不确定我是否认为有必要这样做,但是......

通过挖掘SwingWorker源代码后,工作人员使用private getWorkersExecutorService方法获取当前JVM的ExecutorService

这使用AppContext来存储ExecutorService的实例,SwingWorker的所有实例(对于JVM)将从中绘制线程以执行其功能。

所以,如果你这样做......

AppContext appContext = AppContext.getAppContext();
ExecutorService executorService = (ExecutorService) appContext.get(SwingWorker.class);
System.out.println(executorService);

将打印出类似......

的内容
java.util.concurrent.ThreadPoolExecutor@4554617c[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]

(假设您之前已开始SwingWorker,否则会返回null

但是,这是什么意思?好吧,我们现在可以直接访问所有ExecutorService的{​​{1}}(天启可能现在开始)

这意味着您可以关闭服务,甚至将其从SwingWorker中删除,例如......

AppContext

在我的测试中,它输出......

SwingWorker worker = new SwingWorker() {
    @Override
    protected Object doInBackground() throws Exception {
        System.out.println("Starting");
        Thread.sleep(10000);
        System.out.println("Ending");
        return null;
    }
};
worker.execute();
synchronized (SwingWorker.class) {
    AppContext appContext = AppContext.getAppContext();
    ExecutorService executorService = (ExecutorService) appContext.get(SwingWorker.class);
    System.out.println(executorService);
    System.out.println("Shutting down");
    executorService.shutdownNow();
    try {
        System.out.println("Waiting");
        executorService.awaitTermination(Integer.MAX_VALUE, TimeUnit.DAYS);
        System.out.println("Done");
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
    appContext.remove(SwingWorker.class);
}

所以工人甚至没有机会开始。如果我延迟(在调用java.util.concurrent.ThreadPoolExecutor@4554617c[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0] Shutting down Waiting Starting Done 之后),execute基本上会扼杀它并且不会等待工作人员完成,但是如果我使用shutdownNow它会这样做,那么,那里你有它。

我应该补充一点,AFAIK shutdown意味着他们什么都不做。在WAITING的情况下,这些线程被合并,所以,是的,如果可以的话,工作人员将尝试重新使用它们,但是因为在SwingWorker的“正常”操作中,工作者需要处理SwingWorker方法可能爆炸性的事实,它已经设计用来处理它(这就是为什么doInBackground抛出get),所以除非你有一个特殊的用例,我不确定你是否需要解决这个问题,只是说

答案 1 :(得分:1)

这是对MadProgrammer的答案的回应,我感谢他/她。

Jython用户可以访问和使用私有方法。下面,获取所有主题,并获得SW ExecutorService

运行下面的代码确实显示了" SwingWorker ......"由于shutdownNow,线程在末尾的线程列表中消失。这不会阻止3个新线程在最后持续存在," TimerQueue"," AWT-Windows"和#34; Java2D Disposer" ...但这些都在"系统" ThreadGroup,唯一的"主要" group thread剩余是当前的线程,所以我说问题(如果有的话)解决了!

def get_all_threads():
    private_static_method = java.lang.Thread.getDeclaredMethod( 'getThreads' )
    private_static_method.accessible = True
    # invoking a static method: one param, None
    return private_static_method.invoke( None )

def show_all_threads():
    for thread in get_all_threads():
        print( '  # ID %d: thread %s, state %s, alive %s, interrupted %s, thread group %s' % ( 
            thread.id, thread, thread.state, thread.alive, thread.isInterrupted(), thread.threadGroup.name,) )
        if thread is java.lang.Thread.currentThread():
            print( '  # --> current thread' )

class Worker( javax.swing.SwingWorker ):
    def doInBackground( self ):
        print( 'Starting' )
        time.sleep( 3 )
        print( 'Ending' )

Worker().execute()

private_static_method = javax.swing.SwingWorker.getDeclaredMethod( 'getWorkersExecutorService' )
private_static_method.accessible = True
exec_serv = private_static_method.invoke( None )

show_all_threads()
exec_serv.shutdownNow()
print( '# shutDownNow ordered...' )
exec_serv.awaitTermination( java.lang.Integer.MAX_VALUE, java.util.concurrent.TimeUnit.DAYS )
print( '# ...terminated' )
show_all_threads()

# now "reset" the AppContext object so future executes won't raise a RejectedExecutionException
app_context = sun.awt.AppContext.getAppContext()
app_context.remove( javax.swing.SwingWorker )