【问题标题】:Exception on SwingWorker is not catchableSwingWorker 上的异常不可捕获
【发布时间】:2012-02-07 07:15:52
【问题描述】:

我一直在使用 Java 线程,以便为在管道中运行进程提供 GUI 平台。我已经设法解决了SwingWorker 的一些问题,但这一个似乎难以理解。

我的SwingWorker 看起来像:

SwingWorker<Boolean,Object> worker = new SwingWorker<Boolean,Object>() {
         @Override 
        public Boolean doInBackground() {
             return launchBlockingPipelineProcess(process, instance, project, logger, state);
         }

         @Override
         protected void done(){
             boolean success = false;
             try{
                 success = get();
                 if (!success){
                     state.setTaskFailed(true);
                     }
                 if (process.getStatus().equals(Status.Interrupted)){
                     state.setTaskInterrupted(true);
                     }
             }catch (Exception ex){
                state.setTaskFailed(true); 
                }

             processCompleted(process, success, state);
         }

    };

我用它来运行一个Java进程;启动代码的简化版本是:

try{
    Class<?> target_class = Class.forName(main_class);

    CommandInstance instance = (CommandInstance)target_class.newInstance();
    CommandFunctions.ProcessState state = instance.execute(args);

}catch (InvocationTargetException e){
    throw new PipelineException("Java process '" + this.getName() + "." + uid + "' encountered exception: " + e.getCause().getMessage());
}catch (Exception e){
    throw new PipelineException("JavaProcess encountered exception invoking target: " + e.getMessage());
    }

进程本身在代码周围有一个 try-catch 块,可以从文件中加载一些数据。然而,尽管被包裹在两层 try-catch 块中(实际上是三层,如果你计算 done() 方法),当加载器抛出异常时,它会打印堆栈跟踪,异常没有被捕获,SwingWorker线程挂起(代码停止,因此我无法再强制中断)。

这种异常挂起在其他情况下也发生过;最令人费解的是,在其他看似相同的情况下,异常被捕获并且线程优雅地退出。

虽然我会继续搜索,但我无法在网上找到很多关于此的内容。我不是 Swing 线程方面的专家,所以我希望有人可能对这类问题有所了解。我希望这是我的一个非常愚蠢的错误:)

编辑:@Adrian,这是堆栈跟踪。好像中途停顿了……很奇怪:

java.io.EOFException
    at java.io.RandomAccessFile.readFully(RandomAccessFile.java:399)
    at mgui.io.standard.nifti.Nifti1Dataset.readVolBlob(Nifti1Dataset.java:2179)
    at mgui.io.standard.nifti.Nifti1Dataset.readDoubleVol(Nifti1Dataset.java:1916)
    at mgui.io.standard.nifti.NiftiVolumeLoader.setGrid3DBlocking(NiftiVolumeLoader.java:186)
    at mgui.io.domestic.shapes.VolumeFileLoader.setGrid3D(VolumeFileLoader.java:237)
    at mgui.io.domestic.shapes.VolumeFileLoader.getGrid3D(VolumeFileLoader.java:139)
    at mgui.io.domestic.shapes.VolumeFileLoader.getGrid3D(VolumeFileLoader.java:97)
    at minc.MincFunctions.create_volume_atlas_masks(MincFunctions.java:5240)
    at minc.MincFunctions.run_command(MincFunctions.java:153)
    at mgui.command.CommandInstance.execute(CommandInstance.java:87)
    at mgui.pipelines.JavaProcess.run(JavaProcess.java:141)
    at mgui.pipelines.PipelineFunctions.launchBlockingPipelineProcess(PipelineFunctions.java:238)
    at mgui.pipelines.PipelineFunctions.launchPipelineProcess(PipelineFunctions.ja

EDIT2:在 Eclipse 中进行调试,我可以在抛出它的行的断点处停止(好吧,前一步);那时的堆栈跟踪是:

Nifti1Dataset.readVolBlob(short) line: 2179 
Nifti1Dataset.readDoubleVol(short) line: 1916   
NiftiVolumeLoader.setGrid3DBlocking(Grid3D, int, ProgressUpdater) line: 186 
NiftiVolumeLoader(VolumeFileLoader).setGrid3D(Grid3D, int, ProgressUpdater) line: 237   
NiftiVolumeLoader(VolumeFileLoader).getGrid3D(VolumeInputOptions, int, ProgressUpdater) line: 139   
NiftiVolumeLoader(VolumeFileLoader).getGrid3D(int) line: 97 
MincFunctions.create_volume_atlas_masks() line: 5278    
MincFunctions.run_command(String) line: 153 
MincFunctions(CommandInstance).execute(String[]) line: 87   
JavaProcess.run(String[], long) line: 141   
PipelineFunctions.launchBlockingPipelineProcess(PipelineProcessInstance, String, InterfaceProject, String, PipelineState) line: 238 
PipelineFunctions.launchPipelineProcess(PipelineProcessInstance, String, InterfaceProject, String, boolean, PipelineState) line: 78 
PipelineFunctions.launchPipelineProcess(PipelineProcessInstance, boolean, PipelineState) line: 52   
PipelineProcessInstance.launch(boolean) line: 187   
InterfacePipeline.launch(boolean) line: 388 
PipelineLauncher.doInBackground() line: 57  
PipelineLauncher.doInBackground() line: 1   
SwingWorker$1.call() line: 277  
FutureTask$Sync.innerRun() line: 303    
SwingWorker$2(FutureTask<V>).run() line: 138    
PipelineLauncher(SwingWorker<T,V>).run() line: 316  
ThreadPoolExecutor$Worker.runTask(Runnable) line: 886   
ThreadPoolExecutor$Worker.run() line: 908   
Thread.run() line: 662  

【问题讨论】:

  • 可能是错误而不是异常。你检查过吗?请打印堆栈跟踪。
  • @Adrian 错误的实现只返回 Got exception

标签: java multithreading swing exception-handling swingworker


【解决方案1】:

是的,可以从SwingWorker 的方法done() 中获取Exception(s),但需要严格命名每个线程,更多在这个thread,尤其是answer by @trashgod,我还没有找到另一种可能的方法换一种方式

【讨论】:

  • 感谢您的回复;我对命名线程的重要性有点困惑?在我的情况下,我将 done() 命令包装在 try-catch 块中,并且它没有被线程本身或原始 EDT 调用捕获。
  • 也就是说,我什至没有得到java.util.concurrent.ExecutionException
  • 你是怎么模拟这个异常的,因为所有的Exeptions都被重定向到了SwingWorker,肯定还有一个问题,你是用GC杀死了这个线程还是一些木头
  • 不,我没有杀死它或在其上使用任何实用程序,只是将它作为 SwingWorker 运行,它提供了这个中止的堆栈跟踪。我可以在抛出异常的行的断点处停止;我将该堆栈跟踪添加到 OP。
【解决方案2】:

我通过在 AWT 线程绘制 UI 时不干预解决了类似的表格问题。 我使用invokeLater() 将我的线程与 UI 线程同步。

更多详情请见EventQueue

【讨论】:

    【解决方案3】:

    @Adrian,感谢您的建议;我认为我不会直接从SwingWorker 线程进行任何 UI 更新;我确实对JTree 节点进行了更新以指示进程的成功或失败,但我为此使用了发布/进程机制,这应确保从 EDT 调用所有 UI 更新:

    这些是从执行管道调用的侦听器处理程序:

    @Override
    public void pipelineTaskTerminated(DynamicPipelineEvent event, PipelineTask task) {
        publish(task);
    }
    
    @Override
    public void pipelineTaskUpdated(DynamicPipelineEvent event, PipelineTask task) {
        // Here we can publish the update to a task status
        publish(task);
    
    }
    

    这里是处理方法:

    @Override
    protected void process(List<PipelineTask> tasks) {
    
        // Update task listeners on the Event-Dispatch Thread
        for (int i = 0; i < tasks.size(); i++){
            PipelineTask task = tasks.get(i);
            InterfacePipeline pipeline = task.getPipeline();
            if (pipeline != null){
                task.fireStatusChanged();
                }
            }
    
    }
    

    最后,这是JTree 处理事件的方式:

    public void taskStatusChanged(PipelineTaskEvent e){
        if (model == null) return;
        model.nodeStructureChanged(this);
        tree.repaint();
    }
    

    除此之外(可能提供信息),对JTree 的调用也不会导致 UI 更新,尽管它是从 EDT 调用的。强制树更新的唯一方法是单击节点本身。可能是新线程的问题,但可能是相关的。

    我将通过消除线程中任何潜在的 UI 更新来完成代码和实验,看看是否可以通过这种方式防止问题...敬请期待 :)

    编辑:即使完全删除这些更新,我也会抛出相同的异常......

    【讨论】:

    • 只是为了确保,没有应用一些自定义外观stackoverflow.com/a/3954646/714968,其中一些需要使用invokeAndWait()
    • 谢谢,我使用的是自定义 L&F,但我只是将其设置为默认值并得到相同的问题(异常停止和缺少树更新)。感谢所有的回应!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-04
    • 2013-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多