SwingWorker зависает после завершения: является ли плохой практикой запуск и запуск пула потоков из SwingWorker?

Предположим, что приложение читает файл данных, получает некоторый ввод (через графический интерфейс) от пользователя, получает дополнительные данные из большой базы данных и выполняет длительный анализ.

По совету я удалил трудоемкие части (управление базой данных и фактический анализ) в SwingWorker экземплярах. Теперь первая часть, которая осуществляет управление БД, работает нормально (см. код ниже):

dialog.addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosed(WindowEvent e) {
            final File dbFile = dialog.getFile();

            frame.activateDisGlass("Loading DB, please wait...");
            SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
                @Override
                protected Void doInBackground() throws Exception {
                    try {
                        dbman = new DbManager(dbFile);
                    } catch (SQLException e1) {
                        System.err.println("Unable to load the chosen database, see details below:");
                        e1.printStackTrace();
                    }
                    return null;
                }

                @Override
                protected void done(){
                    frame.deactivateDisGlass();
                }
            };              
            worker.execute();           
        }
    });

Однако та же стратегия не работает должным образом в случае анализа. Поскольку часть анализа довольно длинная и включает в себя множество пользовательских классов, я подытожу ситуацию. Следующий фрагмент кода находится в пользовательском классе, который взаимодействует с графическим интерфейсом для получения пользовательского ввода, вычисления запускаются с помощью analyzeButton, который инициирует создание нескольких объектов, один из которых содержит пул потоков для параллельного выполнения независимого анализа. :

// INITIATE ANALYSIS
    analyzeButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent evt) {

            for (int i = 0; i < annots.length; i++) {
                annots[i] = (ANNOT_TYPE) combos[i].getSelectedItem();
            }

            // Validate the column annotations
            List<ANNOT_TYPE> annotlist = Arrays.asList(annots);
            if (!annotlist.contains(ANNOT_TYPE.R) &&
                !(annotlist.contains(ANNOT_TYPE.OS1) && 
                        annotlist.contains(ANNOT_TYPE.OS2))) {
                JOptionPane
                        .showMessageDialog(
                                null,
                                "<html>Missing annotation!<p>"
                                        + "<p> Please make sure you have annotated the datafile correctly...",
                                "Missing annotation!",
                                JOptionPane.ERROR_MESSAGE);
                return;
            }

            // Validate DB selection
            if (descPanel.getDBManager() == null) {
                JOptionPane
                        .showMessageDialog(
                                null,
                                "<html>No database not selected for analysis! <p>"
                                        + "<p> Please select and load a database..",
                                "Database error", JOptionPane.ERROR_MESSAGE);
                return;
            }

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

            SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
                @Override
                protected Void 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();

                        for (String[] line : data)
                            ds.addRow(line, annots);

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

                        conserv = new ConcurrencyService(ds, descPanel.getDBManager());
                        conserv.serve();
                        DebugToolbox.dumpScores();                          
                        }
                    } catch (InterruptedException e) {
                        logger.severe("Concurrency service interrupted"
                                + System.lineSeparator()
                                + e.getStackTrace()
                                + System.lineSeparator());
                        System.err.println("Interrupt exception!!");
                    }
                    return null;
                }

                @Override
                protected void done() {
                    if(!conserv.isDone())
                        logger.warning("Concurrency Service is not done!" + 
                                System.lineSeparator());

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

            worker.execute();

        }
    });

Так в чем же проблема, спросите вы... Проблема в том, что done() никогда не вызывается на SwingWorker и приложение практически просто зависает в ожидании чего-то. Я вижу, что doInBackground() завершено, так как результаты сбрасываются в файл журнала (это последнее действие в этом методе, но я не вижу ожидаемого результата от done()

Я получил дамп потока от jvisualvm, и вот соответствующий бит:

"SwingWorker-pool-1-thread-2" daemon prio=5 tid=0x00007f8ce5819000 nid=0x860b waiting on condition [0x0000000129593000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x0000000705a2c978> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
    at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

   Locked ownable synchronizers:
    - None

Предыдущий SwingWorker (тот, который загружал БД) имеет точно такое же состояние и трассировку стека, что наводит меня на мысль, что SwingWorker фактически закончил свою работу, но остальная часть приложения почему-то не уведомлена об этом.

Похожие вопросы: я заметил два связанных вопроса с существенными различиями.

  1. Взаимоблокировки JDK-7 SwingWorker? - мои SwingWorker не зависят (или должны быть) друг от друга .

  2. SwingWorker зависает в Unsafe.park() - мой SwingWorker не взаимодействует с графическим интерфейсом или вернуть что-либо (кроме null), так что это не должно иметь ничего общего с get(), верно?

Идеи?


person posdef    schedule 17.07.2014    source источник
comment
На одном из ваших потоков в ThreadPool (или всей системе) висит Event Dispatch Thread, который замораживает все, что там должно выполняться, а именно GUI и код done(). Убедитесь, что ваш ConcurrencyService полностью независим от потока AWT-EventQueue-0.   -  person DSquare    schedule 17.07.2014


Ответы (1)


Вы заявляете:

так что это не должно иметь ничего общего с get(), верно?

Не так. Ключевым моментом является вызов get(), иначе вы не сможете перехватывать и обрабатывать любые исключения, выдаваемые из метода doInBackground SwingWorker. Итак, я рекомендую вам не забывать вызывать get() для SwingWorker в методе done(), возможно, вызывать его первым делом в методе, и не забывать перехватывать и анализировать все исключения, генерируемые этим методом.

person Hovercraft Full Of Eels    schedule 17.07.2014
comment
интересно... Я попробую и отчитаюсь, но спонтанно я ожидаю, что любые исключения, возникающие в doInBackground(), по крайней мере, выдадут что-то в syserr, иначе это будет означать, что любой выброс исключений будет проглочен где-то внутри API, что очень нелогично - person posdef; 17.07.2014
comment
@posdef: Возможно, я неправильно читаю ваш код, но единственное, что я вижу, что может быть выброшено в System.err из doInBackground(), - это InterruptedException, поскольку это единственное исключение, которое вы, кажется, перехватываете в этом методе. - person Hovercraft Full Of Eels; 17.07.2014