Тупики JDK-7 SwingWorker?

У меня есть небольшое приложение для обработки изображений, которое делает несколько вещей одновременно, используя SwingWorker. Однако, если я запускаю следующий код (упрощенный отрывок), он просто зависает на JDK 7 b70 (Windows), но работает в 6u16. Он запускает новый воркер внутри другого воркера и ждет его результата (настоящее приложение запускает несколько подворкеров и ждет весь этот путь). Использовал ли я здесь какие-то неправильные шаблоны (поскольку в основном в пуле свингворкеров есть 3-5 воркеров, который, я думаю, имеет ограничение в 10)?

import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class Swing {
       static SwingWorker<String, Void> getWorker2() {
               return new SwingWorker<String, Void>() {
                       @Override
                       protected String doInBackground() throws Exception {
                               return "Hello World";
                       }
               };
       }
       static void runWorker() {
               SwingWorker<String, Void> worker 
                   = new SwingWorker<String, Void>() {
                       @Override
                       protected String doInBackground() throws Exception {
                               SwingWorker<String, Void> sw2 = getWorker2();
                               sw2.execute();
                               return sw2.get();
                       }
               };
               worker.execute();
               try {
                       System.out.println(worker.get());
               } catch (Exception e) {
                       e.printStackTrace();
               }
       }
       public static void main(String[] args) {
               SwingUtilities.invokeLater(new Runnable() {
                       @Override
                       public void run() {
                               runWorker();
                       }
               });
       }

}

person akarnokd    schedule 27.08.2009    source источник
comment
Вы пытались получить дамп стека?   -  person Tom Hawtin - tackline    schedule 27.08.2009
comment
Кажется, он зависает на sw2.get(), а в jdk7 есть только один поток с именем swingworker. На jdk6 вижу сразу 3-5.   -  person akarnokd    schedule 27.08.2009


Ответы (4)


Поскольку никто еще не отключил ссылку, похоже, это действительно известная ошибка:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6880336

Удивительно, но есть менее 100 голосов за то, что должно быть ошибкой для большинства нетривиальных приложений.

person Trejkaz    schedule 04.05.2010
comment
Да, и пример программы очень похож на мой, не так ли? - person akarnokd; 15.02.2011

Ваши SwingWorkers выполняются в вашем потоке SwingWorker. Итак, когда вы видите

Кажется, он зависает на sw2.get(), а в jdk7 есть только один поток с именем swingworker. На jdk6 вижу сразу 3-5. - кд304

Это связано с тем, что класс SwingWorker является не потоком, а задачей, которая должна выполняться в потоке, и конфигурация по умолчанию для ExecutorService для SwingWorker в Java 6 настроена иначе, чем в Java 7. Т.е. ваш SwingWorkerExecutorService (который определен внутри класса SwingWorker) имеет другое значение для максимального количества потоков, выделяемых задачам.

//From Java 6 SwingWorker

private static final int MAX_WORKER_THREADS = 10;

public final void execute() {
    getWorkersExecutorService().execute(this);
}

private static synchronized ExecutorService getWorkersExecutorService() {
...
private static synchronized ExecutorService getWorkersExecutorService() {
new ThreadPoolExecutor(0, MAX_WORKER_THREADS,
                                     1L, TimeUnit.SECONDS,
                                     new LinkedBlockingQueue<Runnable>(),
                                     threadFactory)
}

У вас есть только один поток, выполняющий задачи SwingWorker, и эта первая задача ожидает завершения второй задачи, которую нельзя запустить, потому что поток, на котором будет выполняться вторая задача, ожидает завершения второй задачи. прежде чем он вернется. Ставить один поток Swingworker в зависимость от выполнения другого — верный путь к тупиковой ситуации. Вы можете рассмотреть возможность использования ExecutorService, чтобы запланировать выполнение событий в потоке SwingWorker и не ставить одно запланированное событие в зависимость от завершения другого запланированного события.

Java 7 SwingWorker

person codethulhu    schedule 27.08.2009
comment
Справедливое объяснение, но почему поведение изменилось? Я ожидаю, что в JDK 7 также будут запущены 3 потока swingworker? Тогда исходное многопоточное выполнение swignworker было ошибкой? - person akarnokd; 28.08.2009

Глядя на исходный код SwingWorker, похоже, что ExecutorService используется как пул рабочих потоков. Возможно, тип используемого ExecutorService изменился между Java 6 и Java 7. Похоже, ваш код заблокируется, если ExecutorService управляет только одним потоком за раз (как вы, кажется, заметили).

Это связано с тем, что ваш вызов 'sw2.get()' заблокирует текущий поток, который будет пытаться использовать sw2. sw2 никогда не может выполняться, потому что первый рабочий блокируется.

Я думаю, что лучшее решение - изменить свою логику, чтобы вы не вызывали цепочки рабочих Swing таким образом.

person Outlaw Programmer    schedule 27.08.2009
comment
Хм, это не похоже на совместимое изменение - если у меня выполняется несколько свингворкеров, они всегда будут работать последовательно на JDK 7 (независимо от многоядерного процессора)? Я думаю, мне нужно изменить внешний свингворкер, например, на обычный поток. - person akarnokd; 27.08.2009

До обновления JDK 18 вы могли запустить:

public static void main(String[] args) {

    new SwingWorker<Void, Void>() {
        @Override
        protected Void doInBackground() throws Exception {
            System.out.println("ok");
            return null;
        }
    }.execute();

}

Этот код больше не работает просто потому, что SwingWorkers должен выполняться в EDT.

Следовательно, вы не можете вкладывать SwingWorkers (sw2 никогда не будет запускаться в вашем образце кода в более новых JDK).

Я предполагаю, что замена вложенных swingWorkers вызовами executorService java.util.concurrent.Future является хорошим обходным путем.

person mimix    schedule 12.02.2010
comment
SwingWorkers не обязательно должны выполняться в EDT. На самом деле, Javadoc SwingWorker явно упоминает текущий поток, из которого порождается SwingWorker, ничего не говоря о том, что это должен быть поток диспетчеризации событий. - person Trejkaz; 04.05.2010