Не отвечающие потоки с участием Swing и AWT-EventQueue - PullRequest
12 голосов
/ 24 мая 2010

У меня есть приложение, которое не отвечает и, кажется, находится в тупике или что-то вроде тупика. Смотрите две темы ниже. Обратите внимание, что поток My-Thread@101c блокирует AWT-EventQueue-0@301. Однако My-Thread только что позвонил java.awt.EventQueue.invokeAndWait(). Так что AWT-EventQueue-0 блоков My-Thread (я верю).

My-Thread@101c, priority=5, in group 'main', status: 'WAIT'
     blocks AWT-EventQueue-0@301
      at java.lang.Object.wait(Object.java:-1)
      at java.lang.Object.wait(Object.java:485)
      at java.awt.EventQueue.invokeAndWait(Unknown Source:-1)
      at javax.swing.SwingUtilities.invokeAndWait(Unknown Source:-1)
      at com.acme.ui.ViewBuilder.renderOnEDT(ViewBuilder.java:157)
        .
        .
        .
      at com.acme.util.Job.run(Job.java:425)
      at java.lang.Thread.run(Unknown Source:-1)

AWT-EventQueue-0@301, priority=6, in group 'main', status: 'MONITOR'
     waiting for My-Thread@101c
      at com.acme.persistence.TransactionalSystemImpl.executeImpl(TransactionalSystemImpl.java:134)
        .
        .
        .
      at com.acme.ui.components.MyTextAreaComponent$MyDocumentListener.insertUpdate(MyTextAreaComponent.java:916)
      at javax.swing.text.AbstractDocument.fireInsertUpdate(Unknown Source:-1)
      at javax.swing.text.AbstractDocument.handleInsertString(Unknown Source:-1)
      at javax.swing.text.AbstractDocument$DefaultFilterBypass.replace(Unknown Source:-1)
      at javax.swing.text.DocumentFilter.replace(Unknown Source:-1)
      at com.acme.ui.components.FilteredDocument$InputDocumentFilter.replace(FilteredDocument.java:204)
      at javax.swing.text.AbstractDocument.replace(Unknown Source:-1)
      at javax.swing.text.JTextComponent.replaceSelection(Unknown Source:-1)
      at javax.swing.text.DefaultEditorKit$DefaultKeyTypedAction.actionPerformed(Unknown Source:-1)
      at javax.swing.SwingUtilities.notifyAction(Unknown Source:-1)
      at javax.swing.JComponent.processKeyBinding(Unknown Source:-1)
      at javax.swing.JComponent.processKeyBindings(Unknown Source:-1)
      at javax.swing.JComponent.processKeyEvent(Unknown Source:-1)
      at java.awt.Component.processEvent(Unknown Source:-1)
      at java.awt.Container.processEvent(Unknown Source:-1)
      at java.awt.Component.dispatchEventImpl(Unknown Source:-1)
      at java.awt.Container.dispatchEventImpl(Unknown Source:-1)
      at java.awt.Component.dispatchEvent(Unknown Source:-1)
      at java.awt.KeyboardFocusManager.redispatchEvent(Unknown Source:-1)
      at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(Unknown Source:-1)
      at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(Unknown Source:-1)
      at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(Unknown Source:-1)
      at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source:-1)
      at java.awt.Component.dispatchEventImpl(Unknown Source:-1)
      at java.awt.Container.dispatchEventImpl(Unknown Source:-1)
      at java.awt.Window.dispatchEventImpl(Unknown Source:-1)
      at java.awt.Component.dispatchEvent(Unknown Source:-1)
      at java.awt.EventQueue.dispatchEvent(Unknown Source:-1)
      at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source:-1)
      at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source:-1)
      at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source:-1)
      at java.awt.EventDispatchThread.pumpEvents(Unknown Source:-1)
      at java.awt.EventDispatchThread.pumpEvents(Unknown Source:-1)
      at java.awt.EventDispatchThread.run(Unknown Source:-1)

Вот метод TransactionalSystemImpl.executeImpl:

private synchronized Object executeImpl(Transaction xact, boolean commit) {
    final Object result;

    try {
        if (commit) { // this is line 134
            clock.latch();
            synchronized(pendingEntries) {
                if (xactLatchCount > 0) {
                    pendingEntries.add(xact);
                } else {
                    xactLog.write(new TransactionEntry(xact, clock.time()));
                }
            }
        }

        final TransactionExecutor executor = transactionExecutorFactory.create(
                xact.getClass().getSimpleName()
        );

        if (executor == null) {
            throw new IllegalStateException("Failed to create transaction executor for transaction: " + xact.getClass().getName());
        }

        result = executor.execute(xact);

    } finally {
        if (commit) clock.unlatch();
    }

    return result;
}

Кто-нибудь знает, что здесь происходит или как это исправить?

Ответы [ 8 ]

6 голосов
/ 03 декабря 2013

Ищем ответ из достоверных и / или официальных источников.

Event Dispatch Thread and EventQueue

Код обработки событий Swing выполняется наспециальный поток, известный как поток обработки событий (EDT).Большая часть кода, который вызывает методы Swing, также выполняется в этом потоке.Это необходимо, поскольку большинство методов объекта Swing не являются поточно-безопасными .Все связанные с графическим интерфейсом задачи, любое обновление должно выполняться в графическом интерфейсе, в то время как процесс рисования должен происходить в EDT, , который включает в себя завершение запроса в событии и обработку его в EventQueue.Затем события отправляются из одной и той же очереди по очереди, в порядке очереди, «ПЕРВЫЙ В ПЕРВЫЙ ВЫХОД».То есть, если Event A ставится в очередь к EventQueue до Event B, то событие B не будет отправлено до события A.

SwingUtilities класс имеет две полезные функции, помогающие с задачей рендеринга GUI:

  • invokeLater(Runnable): заставляет doRun.run() выполняться асинхронно наПоток диспетчеризации событий AWT (EDT).Это произойдет после обработки всех ожидающих событий AWT, как описано выше.
  • invokeAndWait(Runnable): он выполняет ту же функцию, что и invokeLater, но отличается от invokeLater тем, что:
    1. invokeAndWait ожидает завершения задания, заданного им в EDT, перед возвратом.
    2. блокирует (ожидает) текущий (т. Е. Вызывает поток от продолжения выполнения, отправивWAIT состояние посредством синхронизации блокировки.
    3. Он снимет блокировку, как только запрос события, отправленный этой функцией, будет отправлен в EDT, и поток вызова этой функции может продолжиться.

Исходный код имеет доказательства:

  public static void invokeAndWait(Runnable runnable)
    throws InterruptedException, InvocationTargetException {

     if (EventQueue.isDispatchThread()) 
          throw new Error("Cannot call invokeAndWait from the event dispatcher thread");

     class AWTInvocationLock {}
     Object lock = new AWTInvocationLock();

     InvocationEvent event = new InvocationEvent(Toolkit.getDefaultToolkit(), 
                                                 runnable, lock,
                                                 true);

     synchronized (lock) {  //<<---- locking
           Toolkit.getEventQueue().postEvent(event);
           while (!event.isDispatched()) { //<---- checking if the event is dispatched
                  lock.wait(); //<---- if not tell the current invoking thread to wait 
              }
            }

      Throwable eventThrowable = event.getThrowable();
      if (eventThrowable != null) {
          throw new InvocationTargetException(eventThrowable);
      }
  }

Это объясняет проблему:

My-Thread только что вызвал java.awt.EventQueue.invokeAndWait (). Итак, AWT-EventQueue-0 блокирует My-Thread (я верю).

Чтобы объяснить сценарий тупика, который у вас, вероятно, есть, давайте рассмотрим пример:

class ExampleClass 
{

    public synchronized void renderInEDT(final Thread t)
    {
            try {
                SwingUtilities.invokeAndWait(new Runnable() {

                    @Override
                    public void run() {
                        System.out.println("Executinf invokeWait's Runnable ");
                        System.out.println("invokeWait invoking Thread's state: "+t.getState());

                        doOtherJob();
                    }
                });
            } catch (InterruptedException ex) {
                Logger.getLogger(SwingUtilitiesTest.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvocationTargetException ex) {
                Logger.getLogger(SwingUtilitiesTest.class.getName()).log(Level.SEVERE, null, ex);
            }

    }

    public synchronized void renderInEDT2(final Thread t)
    {
                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        System.out.println("Executing invokeLater's Runnable ");
                        System.out.println("invokeLater's invoking Thread's state: "+t.getState());
                        doOtherJob();
                    }
                });
        try {
            Thread.sleep(3000);
        } catch (InterruptedException ex) {
            Logger.getLogger(ExampleClass.class.getName()).log(Level.SEVERE, null, ex);
        }
    }


    public synchronized void doOtherJob()
    {
        System.out.println("Executing a job inside EDT");
    }
}

Как вы сПонимаете, я объявил три синхронизированные функции:

  1. renderInEDT(final Thread t): для выполнения запускаемой задачи в EDT с помощью SwingUtilities.invokeAndWait
  2. renderInEDT2(final Thread t): выполнить задачу Runnable в EDT с помощью SwingUtilities.invokeLater
  3. doOtherJob(): эта функция вызывается каждым из двух вышеупомянутых функций Runnable 'S run().

Передается ссылка на вызывающий поток, чтобы проверить состояние для каждого вызова функции SwingUtilities.Теперь, если мы вызовем renderInEDT() для экземпляра exmpleClass из ExampleClass: здесь Thread t объявлено в контексте класса:

    t =  new Thread("TestThread"){
                 @Override
                 public void run() {
                    exmpleClass.renderInEDT(t);
                 }

    };
    t.start();

Вывод будет:

Executing invokeWait's Runnable 
invokeWait invoking Thread's state: WAITING

Метод doOtherJob() никогда не выполняется в EDT, отправленном SwingUtilities.invokeAndWait, поскольку возникает ситуация взаимоблокировки.Поскольку renderInEDT() синхронизируется и выполняется внутри потока, а именно t, EDT нужно будет ждать выполнения doOtherJob(), пока не будет завершен первый вызывающий поток, выполняющий метод renderInEDT(final Thread t), как описано в official tutorial source of synchronized method:

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

Следовательно, EDT ожидает, пока поток t завершит (и приостановит) выполнение, но поток t фактически заблокирован и отправлен в состояние ожидания методом SwingUtilities.invokeAndWait, как описано выше, поэтому он не может завершитьего выполнение: Bth от EDT и поток t ожидает завершения друг друга с их выполнением .

Давайте рассмотрим приведенный выше пример с регистром: если мы разместим задачу события, используя SwingUtilities.invokeLater, как будет очевидно, если мы выполним функцию renderInEDT2() в экземпляре exampleClass из потока:

  t =  new Thread("TestThread"){
                 @Override
                 public void run() {
                    exmpleClass.renderInEDT2(t);
                 }   
    };
  t.start();

На этот раз вы увидите, что вызов функции продолжает нормально выдавать следующий вывод:

Executing invokeLater's Runnable 
invokeLater's invoking Thread's state: TIMED_WAITING
Executing a job inside EDT

На этот раз doOtherJob() выполняется EDT, как только первый вызывающий поток renderInEDT2()выполнил его выполнение: чтобы подчеркнуть, я уложил поток в спящий режим (3 с), чтобы проверить время выполнения, и, следовательно, он показывает состояние TIMED_WAITING.

Это то, что объясняет вашу вторую проблему: как говорит исключениеа также упомянутое вами в одном из ваших комментариев:

enderOnEDT синхронизируется на каком-то пути вверх в стеке вызовов, метод com.acme.persistence.TransactionalSystemImpl.executeImpl, который синхронизируется.И renderOnEDT ожидает ввода того же метода.Итак, это источник тупика, на который это похоже.Теперь я должен выяснить, как это исправить.

Однако ,

  • SwingUtilities.invokeAndWait(Runnable) особенно используется, когда мы хотим заблокировать или ожидаемотправьте запрос и запросите подтверждение пользователя, если мы продолжим использовать JOptionPane/JDialogue/JFileChooser и т. д.
  • В противном случае, чтобы опубликовать задачу визуализации графического интерфейса в EventQueue, используйте SwingUtilities.invokeLater(Runnable).
  • Хотя я ждал EDT, используя Thread.sleep(time) для демонстрационных целей , пожалуйста, не делайте ничего такого, что могло бы блокировать EDT на заметное количество времени, даже если этонемногие, иначе ваш Swing замерзнет и заставит вас его убить.
  • Мы не должны выполнять какие-либо вычислительные операции, операции чтения / записи или любые другие вещи, не связанные с задачей рендеринга GUI внутри EDT.иначе, внутри Runnable функции invokeAndWait и invokeLater.

На этом этапе вы сами должны иметь возможность выяснить и решить свою проблему, потому что вы не предоставляете достаточно информации длянам вашего кода.Я думаю, что при публикации задачи рендеринга GUI в очереди событий с использованием синхронизированной функции, такой как enderOnEDT, как вы сказали в комментарии, я не вижу никакой причины для вызова другой синхронизированной функции из ее Runnable.Скорее поместите вашу функцию рендеринга в Runnable напрямую.Это моя единственная цель объяснения очереди событий и механизма EDT.

Ссылка:

  1. The Event Dispatch Thread
  2. Class EventQueue
  3. Initial Threads
  4. SwingUtilities.invokeAndWait(Runnable doRun) documentation
4 голосов
/ 06 декабря 2013

Мне, похоже, хорошо известно среди разработчиков Swing моего знакомого, что invokeAndWait проблематично, но, возможно, это не так хорошо известно, как я думал. Кажется, я вспомнил, что видел строгие предупреждения в документации о трудностях в правильном использовании invokeAndWait, но мне трудно что-то найти. Я не могу найти ничего в текущей, официальной документации. Единственное, что мне удалось найти - это строка из старой версии Swing Tutorial от 2005 : (веб-архив)

Если вы используете invokeAndWait, убедитесь, что поток, вызывающий invokeAndWait, не содержит блокировок, которые могут понадобиться другим потокам во время вызова.

К сожалению, эта строка, похоже, исчезла из текущего учебника по Swing. Даже это скорее преуменьшение; Я бы предпочел, чтобы в нем было что-то вроде: «Если вы используете invokeAndWait, поток, вызывающий invokeAndWait , не должен содержать какие-либо блокировки, которые могут понадобиться другим потокам во время вызова». В общем, трудно понять, какие блокировки могут понадобиться другим потокам в любой момент времени, поэтому наиболее безопасной политикой, вероятно, является обеспечение того, чтобы поток, вызывающий invokeAndWait , вообще не удерживал никаких блокировок .

(Это довольно сложно сделать, и поэтому я сказал выше, что invokeAndWait проблематично. Я также знаю, что разработчики JavaFX - по сути, замена Swing - определены в javafx.application. Платформа классифицирует метод с именем runLater, который функционально эквивалентен invokeLater. Но они сознательно пропустили эквивалентный метод invokeAndWait, потому что его очень трудно правильно использовать.)

Причина довольно проста, чтобы быть выведенной из первых принципов. Рассмотрим систему, аналогичную описанной OP, с двумя потоками: MyThread и Thread Dispatch Thread (EDT). MyThread блокирует объект L и затем вызывает invokeAndWait. Это публикует событие E1 и ожидает его обработки EDT. Предположим, что обработчик E1 должен заблокировать L. Когда EDT обрабатывает событие E1, он пытается захватить блокировку L. Эта блокировка уже удерживается MyThread, которая не освобождает его до тех пор, пока EDT не обработает E1, но эта обработка блокируется. по MyThread. Таким образом, мы зашли в тупик.

Вот вариант этого сценария. Предположим, мы гарантируем, что обработка E1 не требует блокировки L. Будет ли это безопасно? Нет. Проблема все еще может возникать, если непосредственно перед тем, как MyThread вызывает invokeAndWait, событие E0 отправляется в очередь событий, и обработчик E0 требует блокировки на L. Как и раньше, MyThread удерживает блокировку на L, поэтому обработка E0 заблокирован. E1 отстает от E0 в очереди событий, поэтому обработка E1 также блокируется. Поскольку MyThread ожидает обработки E1 и блокируется E0, который, в свою очередь, блокируется в ожидании снятия блокировки MyThread с L, у нас снова тупик.

Это звучит довольно похоже на то, что происходит в приложении ОП. Согласно комментариям ОП на этот ответ ,

Да, renderOnEDT синхронизируется по какому-либо пути в стеке вызовов, метод com.acme.persistence.TransactionalSystemImpl.executeImpl, который синхронизируется. И renderOnEDT ожидает ввода того же метода. Итак, это источник тупика, на который это похоже. Теперь я должен выяснить, как это исправить.

У нас нет полной картины, но этого, вероятно, достаточно, чтобы продолжить. renderOnEDT вызывается из MyThread, который удерживает блокировку чего-либо, пока он заблокирован в invokeAndWait. Он ожидает обработки события EDT, но мы можем видеть, что EDT заблокирован чем-то, удерживаемым MyThread. Мы не можем точно сказать, какой именно это объект, но это не имеет значения - EDT явно заблокирован блокировкой, удерживаемой MyThread, и MyThread явно ожидает, пока EDT обработает событие: таким образом, тупик .

Обратите внимание, что мы можем быть уверены, что EDT в настоящее время не обрабатывает событие, отправленное invokeAndWait (аналог E1 в моем сценарии выше).Если бы это было так, тупик возникал бы каждый раз.Кажется, что это происходит только иногда, и согласно комментарию от OP на этот ответ , когда пользователь быстро печатает.Так что я бы поспорил, что событие, обрабатываемое в настоящее время EDT, является нажатием клавиши, которое, как оказалось, было отправлено в очередь событий после того, как MyThread захватил свою блокировку, но до того, как MyThread вызвал invokeAndWait для отправки E1 в очередь событий, таким образом, это аналогдо E0 в моем сценарии выше.

До сих пор это, вероятно, в основном повторение проблемы, сложенное из других ответов и из комментариев ОП к этим ответам.Прежде чем мы перейдем к разговору о решении, вот несколько предположений, которые я делаю относительно приложения OP:

  • Это многопоточное, поэтому для правильной работы различные объекты должны быть синхронизированы.Это включает вызовы от обработчиков событий Swing, которые предположительно обновляют некоторую модель, основанную на взаимодействии с пользователем, и эта модель также обрабатывается рабочими потоками, такими как MyThread.Поэтому они должны правильно блокировать такие объекты.Удаление синхронизации определенно позволит избежать взаимных блокировок, но другие ошибки будут появляться, поскольку структуры данных повреждены несинхронизированным параллельным доступом.

  • Приложение не обязательно выполняет длительные операции с EDT,Это типичная проблема с приложениями с графическим интерфейсом, но, похоже, это не происходит здесь.Я предполагаю, что приложение работает нормально в большинстве случаев, когда событие, обработанное в EDT, захватывает блокировку, обновляет что-то, а затем снимает блокировку.Проблема возникает, когда он не может получить блокировку, потому что держатель замка заблокирован на EDT.

  • Изменение invokeAndWait на invokeLater не вариант.ОП сказал, что это вызывает другие проблемы.Это не удивительно, так как это изменение приводит к тому, что выполнение происходит в другом порядке, поэтому оно даст разные результаты.Я предполагаю, что они были бы неприемлемы.

Если мы не можем удалить блокировки и не можем изменить на invokeLater, нам остается безопасно вызвать invokeAndWait,А «безопасно» означает отказ от замков перед их вызовом.Это может быть произвольно трудно сделать, учитывая организацию приложения OP, но я думаю, что это единственный способ продолжить.

Давайте посмотрим, что делает MyThread.Это значительно упрощается, так как в стеке, вероятно, есть куча промежуточных вызовов методов, но, по сути, это что-то вроде этого:

synchronized (someObject) {
    // code block 1
    SwingUtilities.invokeAndWait(handler);
    // code block 2
}

Проблема возникает, когда какое-то событие проскальзывает в очередь перед обработчиком,и обработка этого события требует блокировки someObject.Как мы можем избежать этой проблемы?Вы не можете отказаться от одной из встроенных в Java блокировок монитора в пределах блока synchronized, поэтому вам необходимо закрыть блок, сделать вызов и снова открыть его:

synchronized (someObject) {
    // code block 1
}

SwingUtilities.invokeAndWait(handler);

synchronized (someObject) {
    // code block 2
}

Это может быть произвольнотрудно, если блокировка на someObject поднимается довольно далеко вверх по стеку вызовов от вызова до invokeAndWait, но я думаю, что выполнение этого рефакторинга неизбежно.

Есть и другие подводные камни.Если кодовый блок 2 зависит от некоторого состояния, загруженного кодовым блоком 1, это состояние может быть устаревшим, так как блок 2 временного кода снова блокируется.Это подразумевает, что кодовый блок 2 должен перезагрузить любое состояние из синхронизированного объекта.Он не должен делать какие-либо предположения на основе результатов из блока кода 1, поскольку эти результаты могут быть устаревшими.

Вот еще одна проблема.Предположим, что обработчик, запускаемый invokeAndWait, требует некоторого состояния, загруженного из общего объекта, например,

synchronized (someObject) {
    // code block 1
    SwingUtilities.invokeAndWait(handler(state1, state2));
    // code block 2
}

Вы не можете просто перенести вызов invokeAndWait из синхронизированного блока, так как это будеттребовать несинхронизированного доступа, получая state1 и state2.Вместо этого вы должны загрузить это состояние в локальные переменные, находясь в пределах блокировки, а затем выполнить вызов, используя эти локальные объекты после снятия блокировки.Что-то вроде:

int localState1;
String localState2;
synchronized (someObject) {
    // code block 1
    localState1 = state1;
    localState2 = state2;
}

SwingUtilities.invokeAndWait(handler(localState1, localState2));

synchronized (someObject) {
    // code block 2
}

Техника совершения звонков после снятия блокировок называется open call . См. Даг Ли, Параллельное программирование в Java (2-е издание), с. 2.4.1.3. Существует также хорошее обсуждение этой техники в работе Гетц и соавт. al., Java-параллелизм на практике , с. 10.1.4. Фактически весь раздел 10.1 довольно подробно описывает тупик; Я очень рекомендую это.

Таким образом, я считаю, что использование методов, которые я описал выше или в цитированных книгах, решит эту тупиковую проблему правильно и безопасно. Однако я уверен, что это потребует тщательного анализа и сложной реструктуризации. Я не вижу альтернативы.

(Наконец, я должен сказать, что, хотя я являюсь сотрудником Oracle, это никоим образом не является официальным заявлением Oracle.)


UPDATE

Я подумал о еще нескольких потенциальных рефакторингах, которые могли бы помочь решить проблему. Давайте пересмотрим исходную схему кода:

synchronized (someObject) {
    // code block 1
    SwingUtilities.invokeAndWait(handler);
    // code block 2
}

Выполняет кодовый блок 1, обработчик и кодовый блок 2 по порядку. Если бы мы изменили вызов invokeAndWait на invokeLater, обработчик был бы выполнен после блока кода 2. Можно легко увидеть, что это будет проблемой для приложения. Вместо этого, как насчет перемещения блока кода 2 в invokeAndWait, чтобы он выполнялся в правильном порядке, но все еще в потоке событий?

synchronized (someObject) {
    // code block 1
}

SwingUtilities.invokeAndWait(Runnable {
    synchronized (someObject) {
        handler();
        // code block 2
    }
});

Вот другой подход. Я не знаю точно, для чего предназначен обработчик, переданный invokeAndWait. Но одной из причин, по которой он может быть invokeAndWait, является то, что он считывает некоторую информацию из GUI и затем использует ее для обновления общего состояния. Это должно быть в EDT, так как он взаимодействует с объектами GUI, и invokeLater не может использоваться, поскольку это может произойти в неправильном порядке. Это предполагает вызов invokeAndWait перед выполнением другой обработки, чтобы считывать информацию из GUI во временную область, а затем использовать эту временную область для продолжения обработки:

TempState tempState;
SwingUtilities.invokeAndWait(Runnable() {
    synchronized (someObject) {
        handler();
        tempState.update();
    }
);

synchronized (someObject) {
    // code block 1
    // instead of invokeAndWait, use tempState from above
    // code block 2
}
2 голосов
/ 24 мая 2010

Трудно сказать, не видя код, но из трассировки стека кажется, что вы запускаете какой-то транзакционный код из потока диспетчеризации событий.Затем этот код запускает экземпляр My-Thread?EDT может быть заблокирован в ожидании My-Thread из кода транзакции, но My-Thread не может завершить работу, так как ему требуется EDT.

В этом случае вы можете использовать SwingUtilities.invokeLater длярендеринг, поэтому EDT завершает код транзакции, а затем он будет обновлять.Или вы не можете выполнить код транзакции из EDT.Для реальной работы, не связанной с рендерингом, вы должны использовать SwingWorker, чтобы избежать какой-либо тяжелой обработки EDT.

1 голос
/ 19 марта 2017

Если вокруг не работает никаких других тупиков, вы можете преобразовать вызов в EventQueue.invokeLater(Runnable) в блокирующую версию, которая ожидает завершения вашего Runnable:

if (EventQueue.isDispatchThread()) r.run();
else {
    final Lock lock = new ReentrantLock();
    final AtomicBoolean locked = new AtomicBoolean(true);
    final Condition condition = lock.newCondition();

    EventQueue.invokeLater(() -> {
        r.run();
        try {
            lock.lock();
            locked.set(false);
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    });

    try {
        lock.lock();
        while (locked.get())
            condition.await();
    } finally {
        lock.unlock();
    }
}
1 голос
/ 03 декабря 2013

Я подозреваю, что указанная вами строка 134 не является реальной строкой 134 (может быть вызвана устаревшим кодом или некоторыми другими проблемами). Кажется, что 134 ждет монитор, который, скорее всего, означает synchronized(pendingEntries), (или clock.latch(), который, я думаю, является своего рода защелкой обратного отсчета?)

Из трассировки стека поток диспетчеризации событий AWT ожидает монитора, который удерживается MyThread.

Пожалуйста, проверьте базу кода на трассировке стека MyThread. Я полагаю, что где-то он синхронизируется на pendingEntries, затем он использует invokeAndWait, чтобы попросить поток диспетчеризации событий что-то сделать, и в свою очередь поток диспетчеризации событий ожидает pendingEntries, что вызвало тупик.


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

Разделение такого действия на отдельный поток / исполнителя кажется мне лучшим выбором.

1 голос
/ 25 мая 2010

Некоторый поток (я предполагаю My-Thread@101c) равен synchronized в вашем экземпляре TransactionalSystemImpl.Поток пользовательского интерфейса пытается ввести executeImpl, но заблокирован на мониторе synchronized и не может.Где еще используется экземпляр TransactionalSystemImpl (с synchronized записью)?Вероятно, между

  at com.acme.ui.ViewBuilder.renderOnEDT(ViewBuilder.java:157)
    .
    .
    .
  at com.acme.util.Job.run(Job.java:425)
0 голосов
/ 05 декабря 2013

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

Строка 134 не содержит ничего, что могло бы вызвать блокировку, поэтому информация в трассировке стека должна быть отключена. Я предполагаю, что commit - это истина, так как в противном случае код зависал бы при создании исполнителя, что является достаточно сложным для JVM, чтобы не оптимизировать это из трассировки стека. Поэтому линия, на которой он висит, должна быть clock.latch(). Я не знаю, что он делает, но, учитывая структуру try / finally, это должно быть что-то важное, возможно, связанное с многопоточностью.

Тогда "почему оно зависает". Как вы уже заявили, два потока пытаются получить доступ к потоку Swing для некоторой работы, но по крайней мере один никогда не возвращается, что, очевидно, приводит к тупиковой ситуации всех компонентов Swing. Чтобы заблокировать поток Swing, кто-то должен по крайней мере вызвать его, но ни одна строка в представленном коде не делает этого, и снова: сложное предположение.

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

Это оставляет двух кандидатов на эту проблему: один - clock.latch(), который может вызвать проблему, но только если он внутренне выполняет какую-либо форму синхронизации, например, объявлен как synchronized void latch(), хотя я не могу сказать, как это заблокирует, так как слишком мало информации. Но исходя из представленного кода, я предполагаю, что остальная часть программы находится в одинаковой плохой форме, так что это не так уж далеко. Вторая возможность - synchronized(pendingEntries), но опять же: в представленных данных нет доказательств, которые могли бы вызвать это, но на примере все идет как надо.

0 голосов
/ 03 декабря 2013

invokeAndWait не допускается из EDT и должно быть исключение.Но, глядя на stacktrace, это выглядит так, потому что вы используете свой поток-обертку, который позволяет вам вызывать invokeAndWait, но это не правильно.Изменение его на SwingUtilities.invokeLater должно решить эту проблему.

Альтернативное решение: если оно того стоит, вы также можете заглянуть в класс SwingWroker для рабочих потоков.Вот ссылка:

http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html

Просто добавьте некоторую дополнительную информацию о взаимоблокировке: Javadoc invokeAndWait ясно упоминает: «Это произойдет после обработки всех ожидающих событий».Это включает в себя текущее событие вызывает invokeAndWait.invokeAndWait будет ожидать завершения текущего события, а текущее событие ожидает завершения invokeAndWait.Это гарантированный тупик, и поэтому не допускается.

...