1

To better understand how EDT synchronization works along

I have created a simple JUnit3 Testcase - see below. The goal is to wait for two events:

  1. the GUI / JFrame to be created
  2. a modification of a Textfield

First I tried a wait() call on the corresponding Boolean lock objects but that didn't work as expected. Then I tried a loop waiting for the boolean lock content. Both approaches do not work as I would expect.

How needs the code below to be modified to get the expected waiting behaviour?

JUnit Testcase

package com.bitplan.test.common;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import junit.framework.TestCase;

/**
 * test the event dispatching thread handling
 * 
 * @author wf
 *
 */
public class TestEDT extends TestCase {


  private JTextField field;
  private  Boolean modified=new Boolean(false);
  private  Boolean created=new Boolean(false);

  /**
   * test UI handling
   * 
   * @throws InterruptedException
   */
  public void testUI() throws InterruptedException {
    // see
    // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        createAndShowGUI();
      }
    });
    synchronized(created) {
      created.wait();
      /**
      while(!created.isTrue()) {
        Thread.sleep(10);
      } */
    }
    field.getDocument().addDocumentListener(new DocumentListener() {
      public void flagModification() {
        synchronized(modified) {
          modified=true;
          modified.notify();
        }      
      }
      public void insertUpdate(DocumentEvent e) {
        flagModification();
      }

      @Override
      public void removeUpdate(DocumentEvent e) {     
        flagModification();
      }

      @Override
      public void changedUpdate(DocumentEvent e) {
        flagModification();        
      }
    });
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        updateField("fieldcontent");
      }
    });
    synchronized(modified) {
      // https://stackoverflow.com/questions/2536692/a-simple-scenario-using-wait-and-notify-in-java?noredirect=1&lq=1
      modified.wait();
      /**
      while(!modified) {
        Thread.sleep(10);
      } */
    }
  }

  /**
   * update the field with the new content;
   * 
   * @param newContent
   */
  protected void updateField(String newContent) {
    field.setText(newContent);
  }

  /**
   * create and show the given gui
   */
  protected void createAndShowGUI() {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setTitle("Example GUI");
    JPanel panel = new JPanel();
    field = new JTextField(30);
    panel.add(field);
    frame.setContentPane(panel);
    frame.pack();
    frame.setVisible(true);
    synchronized(created) {
      created=true;
      created.notify();
    }
  }
}
Community
  • 1
  • 1
Wolfgang Fahl
  • 15,016
  • 11
  • 93
  • 186
  • You are aware that you would register Listeners to do what the requirement is, right? https://docs.oracle.com/javase/tutorial/uiswing/events/ – Fildor Jul 14 '16 at 10:55
  • Also see: [How to use Textfields](https://docs.oracle.com/javase/tutorial/uiswing/components/textfield.html) – Fildor Jul 14 '16 at 10:59
  • And - what is your expected behavior? What are you trying to do? You have no wait locks in place. Note that created.wait() is interrupted by notify(), Thread.sleep() needs to be interrupted by the interrupt() on the appropriate thread. and waiting for 10ms doesn't seems to be visible either. – gusto2 Jul 14 '16 at 10:59
  • I see now - you create a deadlock on the created lock. Try to use create.wait() instead of sleep and place the SwingUtilities.invokeLater inside the synchronized statement (so you omit the thread race). And finally - updating the JTextFied needs to be dome from the AWS Thread. – gusto2 Jul 14 '16 at 11:03
  • Thanks for the comments. I was on the right track with the Lock class but didn't try the wait approach after already having introduced this class. Originally i was going with Booleans and that doesn't work as explained in the answer below. – Wolfgang Fahl Jul 14 '16 at 12:49

1 Answers1

1

The approach works if a "Lock" class is used. Initially I had used a Boolean and that was not working since these lock objects are replaces by the assignments e.g.

created=true. 

will create a new separate Boolean object so the created.notify() signals a different object and does not stop the wait in the main thread.

The Lock version works. I have changed my question's code back to the original wrong Boolean version to show the point.

package com.bitplan.test.common;

import java.awt.Frame;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import junit.framework.TestCase;

/**
 * test the event dispatching thread handling
 * 
 * @author wf
 *
 */
public class TestEDT extends TestCase {

  public static class Lock {

    boolean value;

    public Lock(boolean value) {
      super();
      this.value = value;
    } 
    /**
     * @return the value
     */
    public boolean isTrue() {
      return value;
    }

    /**
     * @param value the value to set
     */
    public void set(boolean value) {
      this.value = value;
    }
  }
  private JTextField field;
  private Lock modified=new Lock(false);
  private Lock  created=new Lock(false);

  /**
   * test UI handling
   * 
   * @throws InterruptedException
   */
  public void testUI() throws InterruptedException {
    // see
    // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        createAndShowGUI();
      }
    });
    synchronized(created) {
      while(!created.isTrue())
        created.wait();
    }
    field.getDocument().addDocumentListener(new DocumentListener() {
      public void flagModification() {
        synchronized(modified) {
          modified.set(true);
          modified.notify();
        }      
      }
      public void insertUpdate(DocumentEvent e) {
        flagModification();
      }

      @Override
      public void removeUpdate(DocumentEvent e) {     
        flagModification();
      }

      @Override
      public void changedUpdate(DocumentEvent e) {
        flagModification();        
      }
    });
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        updateField("fieldcontent");
      }
    });
    synchronized(modified) {
      while(!modified.isTrue())
      // http://stackoverflow.com/questions/2536692/a-simple-scenario-using-wait-and-notify-in-java?noredirect=1&lq=1
        modified.wait();
    }
  }

  /**
   * update the field with the new content;
   * 
   * @param newContent
   */
  protected void updateField(String newContent) {
    field.setText(newContent);
  }

  /**
   * create and show the given gui
   */
  protected void createAndShowGUI() {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.setTitle("Example GUI");
    JPanel panel = new JPanel();
    field = new JTextField(30);
    panel.add(field);
    frame.setContentPane(panel);
    frame.pack();
    frame.setVisible(true);
    synchronized(created) {
      created.set(true);
      created.notify();
    }
  }
}
Wolfgang Fahl
  • 15,016
  • 11
  • 93
  • 186