java - 与日志框架相关的特殊死锁

标签 java swing concurrency awt deadlock

我有一个基于 GUI 的应用程序,它接收一个文件并以表格格式将其显示给用户,以列注释和一堆参数的形式获取一些输入。然后它相应地解析文件并启动“分析”。

我刚刚发现了一个死锁,这是我以前从未遇到过的。

Found one Java-level deadlock:
=============================
"RMI TCP Connection(5)-130.235.214.23":
  waiting to lock monitor 0x00007fac650875e8 (object 0x0000000793267298, a java.util.logging.ConsoleHandler),
  which is held by "AWT-EventQueue-0"
"AWT-EventQueue-0":
  waiting to lock monitor 0x00007fac65086b98 (object 0x00000006c00dd8d0, a java.io.PrintStream),
  which is held by "SwingWorker-pool-1-thread-3"
"SwingWorker-pool-1-thread-3":
  waiting to lock monitor 0x00007fac65087538 (object 0x00000006c001db48, a java.awt.Component$AWTTreeLock),
  which is held by "AWT-EventQueue-0"

本质上存在解析错误,尝试记录它会导致应用程序完全挂起。有趣的是,在该特定步骤之前和之后,日志记录似乎正常工作。

以下是与分析任务相关的代码部分:

    // Activate progress indicator
    frame.getMainFrame().activateInfiGlass();

    SwingWorker<Map<Analyte,AnalysisResult>, Void> worker = new SwingWorker<Map<Analyte,AnalysisResult>, Void>() {
        @Override
        protected Map<Analyte,AnalysisResult> doInBackground() {
            try {
                // register parameters
                param.addParam(AnalysisParams.value_key,descPanel.getValueTypeComboIndex());
                param.addParam(AnalysisParams.sepchar_key,descPanel.getSepCharComboIndex());
                paramPanel.registerParams();

                StringBuilder sb = new StringBuilder("Data preview completed, initiating analysis...");
                sb.append(System.lineSeparator())
                    .append("... column annotations: ")
                    .append(Arrays.toString(annots));
                logger.info(sb.toString() + System.lineSeparator());

                // Create dataset; to be passed on to SwingWorker which will
                // execute the analysis
                ds = new Dataset();

                String[] line;
                for (int i=0; i < data.length; i++){
                    line = data[i];
                    // If ignore button is clicked, skip row..
                    if(!(Boolean) table.getValueAt(i, 0))
                        ds.addRow(line, annots); // <-- This step is where the parsing exception occurs
                }

                System.out.println("Dataset parsed...");
                logger.info("Dataset parsing complete "
                        + System.lineSeparator() 
                        + ds.toString()
                        + System.lineSeparator());

                visualizeDataset();   
                conserv = new ConcurrencyService(ds, dbMan);
                conserv.serve();

            } catch (InterruptedException e) {
                logger.severe("Concurrency service interrupted"
                        + System.lineSeparator()
                        + DebugToolbox.getStackTraceAsString(e)
                        + System.lineSeparator());
                System.err.println("Interrupt exception!!");
            }
            return conserv.getAnalyzedPaths();
        }

        @Override
        protected void done() {
            try{
                results = get();
                visualizeResults(); 
            }
            catch (InterruptedException ignore) {}
            catch (java.util.concurrent.ExecutionException e) {
                String why = null;
                Throwable cause = e.getCause();
                if (cause != null) {
                    why = cause.getMessage();
                } else {
                    why = e.getMessage();
                }
                System.err.println("Error analysing data: " + why);
            } catch (SQLException e) {
                e.printStackTrace();
            }

            logger.info("#DEBUG: Conserv should have been terminated by now..." + System.lineSeparator());
            frame.getMainFrame().deactivateInfiGlass();
            DebugToolbox.stopExecTimer();               
        }
    };
    worker.execute();
}});

使用方法 addRow()Dataset 实例中对值进行解析。下面这段代码展示了解析错误的处理方式

public double valueToIntensity(String val){
    if(val.equalsIgnoreCase(""))
        return missingVal;

    try{
        double d = Double.parseDouble(val);
        switch(valType){
            case RAW: break;
            case LOG2:  d = StrictMath.pow(2,d); break;
            case LOGN:  d = StrictMath.pow(StrictMath.E, d); break;
            case LOG10: d = StrictMath.pow(10,d); break;
            default: throw new RuntimeException("Unrecognized value type");
        }

        if(Double.isInfinite(d)){
            StringBuilder msg = new StringBuilder("Double precision overflow occurred: 'd' is infinite!!");
            msg.append(System.lineSeparator())
                .append("chosen value scale is ").append(valType)
                .append(System.lineSeparator())
                .append("value = ").append(val);

            logger.severe(msg.toString()  + System.lineSeparator());

            System.err.println("Data parsing error!!" +
                    "Please make sure that you have selected the correct scale...");
            System.exit(FeverMainFrame.exitCodes.get(this.getClass()));         
        }
        else
            return d;

    } catch (NumberFormatException e){
        System.err.println("Data parsing error!!");
        // THE FOLLOWING LINE IS WHERE DEADLOCK OCCURS                  
        logger.severe("Expected: string representation of a numerical value, "
                        + "Found: " + val  + System.lineSeparator());
        System.err.println("Please make sure the datafile does not include any strings "
                                + "like 'N/A' or '-' for denoting missing values.");


        System.exit(FeverMainFrame.exitCodes.get(this.getClass()));         
    }

    // TODO: This should never happen!
    throw new RuntimeException("Assertion failed during dataset parsing...");
}

如果我删除导致解析错误的值,而不更改任何其他内容,则日志记录框架和应用程序的其余部分都会按预期运行。

如果您能了解此特定案例中发生的情况,我将不胜感激。

最佳答案

如果没有完整的示例,请验证您的 doInBackground() 实现不会尝试更新任何 GUI component or model 。相反,publish() 临时结果并在 EDT 可用时 process() 它们。显示了完整的示例 here .

关于java - 与日志框架相关的特殊死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33343577/

相关文章:

Java - Bukkit/Spigot *如何让我的插件需要密码才能启动和运行?*

java - 将 JTextField 与模型结合使用(在 focusLost 上)并使用模型数据运行操作

concurrency - 如何发送消息以在 YAWS/Erlang 中接收

concurrency - cuda修改flag数组的问题

java - 如何使用 Java 中的 SIGAR 或 OSHI API 获取特定应用程序的操作系统进程详细信息?

java - 如何将 util.Date 转换为 time.LocalDate 以正确处理 1893 年之前的日期

java列表实现

Java JFrame .setSize(x, y) 不工作?

java - 在java中的浏览器中打开超链接?

java - 独占锁定ConcurrentHashMap