简体   繁体   English

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

[英]truly “synchronized” method for Event Dispatch Thread

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. 在方法上使用synced关键字一次只能允许一个线程执行该方法,但是EDT可以处理将同时在该方法中运行的多个“事件”。 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 我正在寻找的是一种仅一次允许一个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

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

Okay, well, here is my shot at an answer, guided by support from millimoose :D 好的,这是我的答案,这是在millimoose支持下进行的:D

Let's start with these observations: 让我们从这些观察开始:

  • There is only one EDT thread 只有一个 EDT线程
  • A thread only has one execution context 一个线程只有一个执行上下文
  • A modal dialog blocks current execution context 模态对话框阻止当前执行上下文
  • SwingWorker.done is always invoked on the EDT 总是在EDT上调用SwingWorker.done

Thus, a modal dialog cannot block swing events or it would cause the entire UI to become unresponsive; 因此,模式对话框无法阻止摆动事件 ,否则将导致整个UI变得无响应。 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: 然后,代码执行调用图(在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

Thus, the modal dialogs still process swing events while they run, but in a "recursive" manner. 因此,模式对话框在运行时仍会以“递归”方式处理摆动事件

I have just figure out myself, but as @millimoose commented: 我刚刚弄清楚自己,但是正如@millimoose所说:

The test() method isn't being called concurrently, it's just being called recursively. test()方法不是并发调用的,而是递归调用的。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM