Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I have an application that is unresponsive and seems to be in a deadlock or something like a deadlock. See the two threads below. Notice that the My-Thread@101c thread blocks AWT-EventQueue-0@301. However, My-Thread has just called java.awt.EventQueue.invokeAndWait(). So AWT-EventQueue-0 blocks My-Thread (I believe).

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)

Here is the TransactionalSystemImpl.executeImpl method:

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;
}

Does anyone know what's going on here or how to fix it?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
202 views
Welcome To Ask or Share your Answers For Others

1 Answer

Looking for an answer drawing from credible and/or official sources.

Event Dispatch Thread and EventQueue

Swing event handling code runs on a special thread known as the Event Dispatch Thread(EDT). Most code that invokes Swing methods also runs on this thread. This is necessary because most Swing object methods are not thread safe. All GUI related task, any update should be made to GUI while painting process must happen on the EDT, which involves wrapping the request in an event and processing it onto the EventQueue. Then the event are dispatched from the same queue in the one by one in order they en-queued, FIRST IN FIRST OUT. That is, if Event A is enqueued to the EventQueue before Event B then event B will not be dispatched before event A.

SwingUtilities class has two useful function to help with GUI rendering task:

  • invokeLater(Runnable): Causes doRun.run() to be executed asynchronously on the AWT event dispatching thread(EDT). This will happen after all pending AWT events have been processed, as is described above.
  • invokeAndWait(Runnable): It has the same function as the invokeLater but it differs from invokeLater for the fact that:
    1. invokeAndWait waits for the task given by it to the EDT, to finish before returning.
    2. it blocks(awaits) the current(i.e., it's invoking thread from continuing it's execution by sending to WAIT state by means of synchronizing a lock.
    3. It will release the lock once the event request posted by this function gets dispatched in the EDT and the invoker thread of this function can continue.

The source code has the evidence:

  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);
      }
  }

This explains the issue:

My-Thread has just called java.awt.EventQueue.invokeAndWait(). So AWT-EventQueue-0 blocks My-Thread (I believe).

To explain a scenario of deadlock which you are likely having, lets look into an example:

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");
    }
}

As you can see, i have declared three synchronized function:

  1. renderInEDT(final Thread t): to execute a Runnable task in the EDT bySwingUtilities.invokeAndWait
  2. renderInEDT2(final Thread t): to execute a Runnable task in the EDT by SwingUtilities.invokeLater
  3. doOtherJob() : This function is invoked by each of the above two function's Runnable'S run() method.

The reference of the invoking thread is passed to check the state for each of the invocation of SwingUtilities function. Now, if we invoke renderInEDT() on an instance exmpleClass of ExampleClass: Here Thread t is declared in the class context:

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

    };
    t.start();

The output will be:

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

The doOtherJob() method never gets executed in the EDT posted by SwingUtilities.invokeAndWait because a deadlock situation appears. As the renderInEDT() is synchronized and being executed inside a thread namely t, the EDT will need to wait to execute doOtherJob() until first invoking thread is done executing the renderInEDT(final Thread t) method, as is described in the official tutorial source of synchronized method:

it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

Hence, EDT is waiting for the t thread to finish(and suspend) execution, but thread t is actually blocked and sent to waiting state by the SwingUtilities.invokeAndWait method as described above, hence it is not being able to finish it's execution: Bth of the EDT and the t thread is waiting for each other to be done with their execution.

Let us see the above example with case: if we post event task using SwingUtilities.invokeLater as it will be evident if we execute the renderInEDT2() function on the exampleClass instance from a thread:

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

This time you will see that, the function invocation continues normally producing following output:

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

This time doOtherJob() gets executed by the EDT as soon as the first calling thread of renderInEDT2() is done executing it: to emphasize i have put the thread to sleep (3s) to inspect the execution timing and hence it shows the state TIMED_WAITING.

This is what explains your second issue: As the exception is telling and also mentioned by you in one of your comment:

enderOnEDT is synchronized on something way up in the call stack, the com.acme.persistence.TransactionalSystemImpl.executeImpl method which is synchronized. And renderOnEDT is waiting to enter that same method. So, that is the source of the deadlock it looks like. Now I have to figure out how to fix it.

However,

  • SwingUtilities.invokeAndWait(Runnable) especially used while we want to block or awaits a thread and ask user confirmation if we should continue using JOptionPane/JDialogue/JFileChooser etc.
  • Otherwise, to post a GUI rendering task in the EventQueue, use SwingUtilities.invokeLater(Runnable).
  • Although, i have await the EDT using Thread.sleep(time) for demonstration purposes, Please don't do anything such, which might block the EDT for a mentionable amount of time even if it is few, otherwise your Swing will get frozen and will force you to kill it.
  • We should not perform any kind of computational work or read/write operation or any other thing that is not related to GUI rendering task inside EDT aka, inside the Runnable of the invokeAndWait and invokeLater function.

At this point you yourself should be able to figure out and solve your problem because you are not providing enough details to us of your code. I think while posting your GUI rendering task to Event Queue using a synchronized function like enderOnEDT as you have said in your comment, then I don't see any reason for calling another synchronized function from it's Runnable. Rather put your rendering function in this Runnable directly. That is my sole purpose of explaining the Event Queue and EDT mechanism.

Reference:

  1. The Event Dispatch Thread
  2. Class EventQueue
  3. Initial Threads
  4. SwingUtilities.invokeAndWait(Runnable doRun) documentation

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...