Java – unresponsive threads involving swing and AWT eventqueue

I have an unresponsive application that seems to be in an impasse or stalemate Look at the following two threads Note that the my thread @ 101c thread blocks awt-eventqueue-0 @ 301 However, my thread just called Java awt. EventQueue. invokeAndWait(). So AWT - eventqueue - 0 blocks my thread (I believe it)

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,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)

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

Who knows what happened here or how to solve it?

Solution

Among swing developers I'm familiar with, invokeandwait seems to have a problem, but maybe it's not what I knew before I seem to recall seeing severe warnings in the documentation on the difficulties of using invokeandwait correctly, but it's hard for me to find anything Nothing can be found in the current official documents The only thing I can find is the old version of swing tutorial from 2005: (network file)

Unfortunately, this line seems to have disappeared from the current swing tutorial Even if this is an understatement; I like to say, "if you use invokeandwait, the thread calling invokeandwait cannot hold any locks that other threads may need when the call occurs." In general, it is difficult to know what locks other threads may need at any given time. The safest strategy may be to ensure that threads call invokeandwait and will not save any locks at all

(this is difficult to do, which is why the invokeandwait I mentioned above is problematic. I also know that the designer of JavaFX - basically a swing replacement - defines a method called runlater in the javafx.application.platform class, which is functionally equivalent to invoker, but they deliberately omit an equivalent method to call and call, because it is difficult to use correctly.)

The reason is that it is quite direct from the first principle Consider a system similar to that described by OP, which has two threads: myThread and event scheduling thread (EDT) MyThread locks the object L and then calls invokeAndWait.. This post event E1 and waits for it to be processed by EDT Suppose that the handler of E1 needs to lock L. when EDT processes event E1, it attempts to lock L This lock is saved by myThread. It will not be abandoned before EDT processes E1, but this process is blocked by myThread So we have an impasse

Here are the changes in this situation Suppose we ensure that processing E1 does not require locking L. will this be safe? No If the event E0 is published to the event queue before invokeandwait is called by myThread, and the handler of E0 needs to be locked on L, the problem may still occur As mentioned earlier, myThread will be locked on L, so the processing of E0 is blocked After E1 is in E0 in the event queue, E1 processing is also blocked Since myThread is waiting for E1 to be processed and blocked by E0, it is blocked again, waiting for myThread to give up the lock on L, and we deadlock again

This sounds very similar to what happens in OP applications According to op's comments on this answer,

We don't have a complete picture, but it may be enough to continue RenderOnEDT is being invoked from MyThread. When it is blocked in invokeAndWait, the object is locking something. Waiting for EDT event processing, but we can see that EDT is blocked by something held by myThread We can't completely specify which object, but it doesn't matter - EDT is explicitly blocked by the locking of myThread, and myThread is obviously waiting for EDT to handle the event: therefore, deadlock

Also note that we can be sure that EDT does not currently handle events published by invokeandwait (similar to E1 in my scenario above) If so, there will be an impasse every time It seems to happen only sometimes, according to op's comments in March 2007, when users type quickly So I bet that the event being processed by EDT is a key, which is published to the event queue just after myThread is locked, but E1 is published to the event queue before myThread calls invokeandwait, so it is similar to E0 in my above scenario

So far, this may be a review of questions pieced together from other answers and op's opinions on these answers Before we move on to a solution, here are some of my assumptions about OP applications:

>It is multithreaded, so various objects must be synchronized to work properly This includes calls from swing event handlers, which may update some models based on user interaction, and this model is also handled by worker threads such as myThread Therefore, they must lock these objects correctly Deleting synchronization will definitely avoid deadlock, but other errors will spread as the data structure is destroyed by asynchronous concurrent access. > Applications do not necessarily perform long - running operations on EDT This is a typical problem with GUI applications, but it doesn't seem to happen here I assume that the application works normally in most cases, in which the events handled by EDT seize the lock, update something, and then release the lock This problem occurs when the lock cannot be obtained because the holder of the lock is locked at American Eastern time (ice). > Changing invokeandwait to invoker is not an option The operating procedure indicates that doing so will cause other problems This is not surprising because this change causes execution to occur in a different order, so it will give different results I will assume that they are unacceptable

If we can't delete the lock and we can't change to invoker, we can safely call invokeandwait And "safe" means unlocking before dialing Due to the organization of OP application, this may be arbitrary, but I think it is the only way

Let's see what myThread is doing This is very simple, because there may be a bunch of intervening method calls on the stack, but fundamentally, it is like this:

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

This problem occurs when some events are imperceptible in the queue in front of the handler. The processing of this event needs to lock someobject How can we avoid this problem? You cannot discard one of the Java built-in monitor locks in a synchronization block, so you must close the block, make a call, and open it again:

synchronized (someObject) {
    // code block 1
}

SwingUtilities.invokeAndWait(handler);

synchronized (someObject) {
    // code block 2
}

If the lock on someobject is equivalent to that obtained by the call stack from the call invokeandwait, it may be arbitrarily difficult, but I think such refactoring is inevitable

There are other pitfalls If code block 2 depends on some state loaded by code block 1, the state may have expired when the code block 2 is locked again This means that code block 2 must reload any state from the synchronization object It cannot make any assumptions based on the results of code block 1 because these results may be outdated

This is another problem Suppose that the handler run by invokeandwait needs to load some state from the shared object, for example,

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

You cannot migrate the invokeandwait call from the synchronization block, because it will require asynchronous access to get state1 and State2 What you need to do is load this state into local variables during locking, and then use these local variables to make calls after releasing the lock It's like:

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

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

synchronized (someObject) {
    // code block 2
}

The technology of making a call after releasing the lock is called public call technology See Doug lea, Java parallel programming (2nd Edition), 2.4 Section 1.3 Goetz et al. Also discussed this technology Java concurrency practice, Section 10.1 Section 4 In fact, Section 10.1 comprehensively covers the deadlock; I recommend it very high

In conclusion, I believe that using the techniques described above, or in the cited books, will correctly and safely solve this deadlock problem However, I believe that this requires a lot of detailed analysis and reorganization But I see no alternative

(finally, I should say that although I am an employee of Oracle, this is not an official statement of Oracle.)

UPDATE

I thought of some potential refactorings that might help solve the problem Let's reconsider the original pattern of the code:

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

This executes code block 1, handler, and code block 2 in sequence If we change the invokeandwait call to invoker, the handler will execute after code block 2 It's easy to see that this will be a problem for the application Instead, how do we move block 2 into invokeandwait so that it executes in the correct order, but still on the event thread?

synchronized (someObject) {
    // code block 1
}

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

This is another way I don't know what it means to pass the handler to invokeandwait But one reason you might need invokeandwait is that it reads some information from the GUI and then uses it to update the shared state This must be on the EDT because it interacts with GUI objects and the invoker cannot be used because it will occur in the wrong order This means that invokeAndWait is invoked before other processing is performed so that the information in GUI is read into the temporary area and then processed with the temporary area:

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
}
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>