1

This is simplified code, which would be called from pressing a button in my main JFrame class. Using this code, and then dismissing one of the resulting dialogs, causes all of my active windows in my Java session to either lock up or just go away.

//Broken code
private void buttonActionPerformed(java.awt.event.ActionEvent evt) {
    List<JFrame> frameList = new ArrayList<>();
    frameList.add(new TestJFrame());
    frameList.add(new TestJFrame());

    frameList.forEach(frame -> frame.setVisible(true));

    frameList.forEach(frame -> {
        SwingUtilities.invokeLater(() -> {
            JOptionPane.showMessageDialog(frame, "Msg", "Title", 0);
            frame.setVisible(false);
            frame.dispose();
        });
    });
}

However, if I were to remove the SwingUtilities.invokeLater() section then it works like I would expect (dialog pops up, close the dialog, window goes away, repeat).

//Working code
private void buttonActionPerformed(java.awt.event.ActionEvent evt) {
    List<JFrame> frameList = new ArrayList<>();
    frameList.add(new TestJFrame());
    frameList.add(new TestJFrame());

    frameList.forEach(frame -> frame.setVisible(true));

    frameList.forEach(frame -> {
        //SwingUtilities.invokeLater(() -> {
            JOptionPane.showMessageDialog(frame, "Msg", "Title", 0);
            frame.setVisible(false);
            frame.dispose();
        //});
    });
}

I'd rather not use the second one because the actual code is being started in a background thread that is notifying a set of listeners, so if I were to use the second one then it would block up the thread and slow down listeners until the user responds (when I could be processing during that time). What about the invokeLater() is breaking me? Is this expected behavior?

NOTE: This is simplified code pulled out of how I'm actually using it, but the core issue still exists (I have multiple JFrames, and if multiple JOptionPane.showMessageDialog()s were put on invokeLater()s for different JFrames then it breaks me. I tested the above code with a new, isolated, JFrame class created in Netbeans and was able to reproduce the error.

EDIT: I can't seem to reproduce the error in Windows, only seems to happen in Linux.

billoot
  • 77
  • 15

3 Answers3

0

It is most likely invokeLater() which is breaking your code. If you want to thread this action try using a simple thread or

  EventQueue.invokeLater(new Runnable() 
  {
            public void run() 
            { //Your joptionpane here 
            }   
  });`
Pierre97
  • 43
  • 4
  • According to the Swing documentation, `As of 1.3 this method is just a cover for java.awt.EventQueue.invokeLater()`, so I don't see how this would fix anything? And I can't just use a simple thread because graphics related events need to happen on the EDT. – billoot Jun 14 '17 at 18:10
  • Yes they both should inherently work the same but you never know. As for simple thread in some cases you can get away with using one ex. JTextArea and doing GIF animations. Try using SwingWorker for the EDT [SwingWorker](https://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html) – Pierre97 Jun 14 '17 at 18:28
  • `EventQueue.invokeLater()` had the same results as `SwingUtilities.invokeLater()`. `new SwingWorker() { ... }.execute()` failed in a similar way as well, just with some extra repaint failures. – billoot Jun 14 '17 at 20:37
0

Instead of invoke later i prefer using a 1) simple thread 2) TimerTask 3) ScheduledExecutorService

Use one of these methods. This is an example for using timer task

import java.util.Timer;
import java.util.TimerTask;
public class Task2 {
   public static void main(String[] args) {
     TimerTask task = new TimerTask() {
      @Override
      public void run() {
        // task to run goes here
        System.out.println("Hello !!!");
      }
    };
    Timer timer = new Timer();
    long delay = 0;
    long intevalPeriod = 1 * 1000; 
    // schedules the task to be run in an interval 
    timer.scheduleAtFixedRate(task, delay,
                            intevalPeriod);
  } // end of main
}

If you are not satisfied you can use invoke later. But remember never use invoke and wait its a bad idea

vichu
  • 353
  • 2
  • 10
  • 1
    This is an uninformed answer that doesn't work well for Swing, doesn't fit my question, and doesn't fix my problem. Both the thread recommendation, and the plain wrong statement about `invokeLater`. https://stackoverflow.com/questions/5499921/invokeandwait-method-in-swingutilities. – billoot Jun 20 '17 at 14:25
0

Here is my approach, as I understand the problem is on the locked windows, which are waiting for an event to finish, in swing the events are related with the AWT-EventQueue.

Here is a little explanation about: https://stackoverflow.com/a/22534931/1670134

So, in order to get your window working I used the Future type:

From the java doc:

A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready.

package com.stackoverflow.future;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

import com.stackoverflow.frame.MyFrame;

public class MySwingWorker extends SwingWorker<Void, Void>{

    @Override
    protected Void doInBackground() throws Exception {
        final ExecutorService service = Executors.newFixedThreadPool(1);
        List<Future<JFrame>> frameList = new ArrayList<Future<JFrame>>();
        frameList.add(service.submit(new SwingLoader(new MyFrame())));
        frameList.add(service.submit(new SwingLoader(new MyFrame())));

        try {
            service.shutdown();
            service.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        } finally {
            service.shutdownNow();
        }
        return null;
    }

    public static void main(String[] args) {
        MySwingWorker mySwingWorker = new MySwingWorker();
        SwingUtilities.invokeLater(mySwingWorker);
    }

}

The loader:

package com.stackoverflow.future;

import java.util.concurrent.Callable;

import javax.swing.JFrame;

public class SwingLoader implements Callable<JFrame>{

    private JFrame frame;

    public SwingLoader(JFrame frame){
        this.frame = frame;
    }

    @Override
    public JFrame call() throws Exception {
        frame.setVisible(true);
        return frame;
    }

}

NOTE: This code is just a proto, in order to provide you with ideas and it must be modified and cleaned in order to achieve the desired results.

Here you are a link with a couple of explanations of each type: http://winterbe.com/posts/2015/04/07/java8-concurrency-tutorial-thread-executor-examples/

sirandy
  • 1,834
  • 5
  • 27
  • 32
  • As far as I understand your solution, you don't understand the problem at all. The issue is that my above posted code causes the modal dialogs to interfere with each other. Using a modal dialog should prevent users from interacting with the locked dialogs until it is dismissed. For some reason dismissing one modal dialog is dismissing them both, but it only releases one of the locks from the modal dialogs, causing all applications to lock up. I'm asking why. Your code isn't even thread-safe for swing. – billoot Jun 22 '17 at 17:31