事件调度线程的真正"同步"方法

Mit*_*tch 1 java swing synchronization

在方法上使用synchronized关键字一次只允许一个线程执行该方法,但EDT可以同时处理将在该方法中运行的多个"事件".请参阅下面的示例代码以进行演示.单击测试按钮时,输出为:

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
Run Code Online (Sandbox Code Playgroud)

我正在寻找的方法是一次只允许一个EDT事件在test()方法中处于活动状态,这样输出就会是

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
Run Code Online (Sandbox Code Playgroud)

好像有人必须先解决这个问题.我认为可以在方法的开头编写某种锁定对象,或者包装方法,但是懒惰,宁愿不重新发明轮子.

我的测试用例:

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;
}
Run Code Online (Sandbox Code Playgroud)

use*_*674 5

好吧,这是我在一个答案的镜头,在millimoose的支持下:D

让我们从这些观察开始:

  • 只有一个 EDT线程
  • 线程只有一个执行上下文
  • 模态对话框阻止当前执行上下文
  • 始终在EDT上调用SwingWorker.done

因此,模态对话框不能阻止回转事件,否则会导致整个UI无响应; 那么,它如何在不阻挡挥杆事件的情况下阻挡?

模态对话框运行自己的事件调度循环.然后,代码执行调用图(在完成SwingWorker之后完全在EDT线程上运行)如下所示:

-> 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
Run Code Online (Sandbox Code Playgroud)

因此,模态对话框在它们运行时仍然处理摆动事件,但是以"递归"方式.