Reputation: 1039
Using the synchronized keyword on a method allows only one thread at a time to execute that method, but the EDT can process multiple "events" that will be running in that method concurrently. See sample code below for a demonstration. When you click the test button, the output is:
0 before dialog, EDT=true
1 before dialog, EDT=true
(click OK button for 1 here)
1 after dialog, EDT=true
(click OK button for 0 here)
0 after dialog, EDT=true
What I'm looking for is a way to only allow one EDT event to be active in the test() method at a time, so that the output would be
0 before dialog, EDT=true
(click OK button for 0 here)
0 after dialog, EDT=true
1 before dialog, EDT=true
(click OK button for 1 here)
1 after dialog, EDT=true
Seems like someone must have solved this problem before. I think it would be possible to write some kind of locking object to use at the beginning of the method, or to wrap the method, but being lazy, would rather not reinvent the wheel.
My test case:
package test1;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class EDTSyncTest extends javax.swing.JFrame {
private static final Object locker = new Object();
private int counter;
public EDTSyncTest() {
initComponents();
}
private synchronized void test() {
int l_id = counter++;
logit("" + l_id + " before dialog, EDT=" + SwingUtilities.isEventDispatchThread());
JOptionPane l_pane = new JOptionPane("test id " + l_id);
JDialog l_diag = l_pane.createDialog(this, "test");
l_diag.setModal(true);
l_diag.setVisible(true);
logit("" + l_id + " after dialog, EDT=" + SwingUtilities.isEventDispatchThread());
}
private void startTest() {
new Delayer().execute();
test();
}
private static void logit(String a_msg) {
System.out.println(a_msg);
}
private class Delayer extends SwingWorker<Object, Object> {
@Override
protected Object doInBackground() throws Exception {
Thread.sleep(2000);
return null;
}
@Override
protected void done() {
test();
}
}
private void initComponents() {
jButton1 = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
getContentPane().setLayout(new java.awt.FlowLayout());
jButton1.setText("Test");
jButton1.setName("jButton1"); // NOI18N
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
getContentPane().add(jButton1);
pack();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
startTest();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new EDTSyncTest().setVisible(true);
}
});
}
protected javax.swing.JButton jButton1;
}
Upvotes: 1
Views: 1200
Reputation: 7719
Okay, well, here is my shot at an answer, guided by support from millimoose :D
Let's start with these observations:
Thus, a modal dialog cannot block swing events or it would cause the entire UI to become unresponsive; then, how does it block without blocking swing events?
A modal dialog runs its own event dispatch loop. Then, the code execution call graph (which runs entirely on the EDT thread after completion of the SwingWorker) looks like this:
-> done (process FIRST done)
-> 0/before
-> modal dialog event loop (process NEXT done)
-> 1/before
-> modal dialog event loop (TOP DIALOG)
<- OK PRESSED
<- 1/after
<- OK PRESSED
<- 0/after
<- back to normal EDT event loop
Thus, the modal dialogs still process swing events while they run, but in a "recursive" manner.
Upvotes: 5
Reputation: 9599
I have just figure out myself, but as @millimoose commented:
The
test()
method isn't being called concurrently, it's just being called recursively.
Modify your code a little and you'll see:
logit("" + l_id + " before dialog, EDT="
+ SwingUtilities.isEventDispatchThread());
new Throwable().printStackTrace(System.out);
Output:
0 before dialog, EDT=true
java.lang.Throwable
at EDTSyncTest.test(EDTSyncTest.java:22)
at EDTSyncTest.startTest(EDTSyncTest.java:35)
at EDTSyncTest.jButton1ActionPerformed(EDTSyncTest.java:75)
at EDTSyncTest.access$1(EDTSyncTest.java:74)
at EDTSyncTest$1.actionPerformed(EDTSyncTest.java:66)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
/* ... */
at java.awt.EventDispatchThread.run(Unknown Source)
1 before dialog, EDT=true
java.lang.Throwable
at EDTSyncTest.test(EDTSyncTest.java:22)
at EDTSyncTest.access$0(EDTSyncTest.java:17)
at EDTSyncTest$Delayer.done(EDTSyncTest.java:51)
at javax.swing.SwingWorker$5.run(Unknown Source)
/* ... */
at java.awt.Dialog.setVisible(Unknown Source)
at EDTSyncTest.test(EDTSyncTest.java:27)
at EDTSyncTest.startTest(EDTSyncTest.java:35)
at EDTSyncTest.jButton1ActionPerformed(EDTSyncTest.java:75)
at EDTSyncTest.access$1(EDTSyncTest.java:74)
at EDTSyncTest$1.actionPerformed(EDTSyncTest.java:66)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
/* ... */
at java.awt.EventDispatchThread.run(Unknown Source)
1 after dialog, EDT=true
0 after dialog, EDT=true
Upvotes: 1