Question

I have the following fun which will be executed by non event dispatching thread. In the middle of thread, I want a

  1. A confirmation box pop up. Thread suspend its execution.
  2. User makes a choice.
  3. Thread will get the choice and continue execution.

However, I find out it is not easy to do it in thread safety way, as dialog box should be shown by event dispatching thread. I try

public int fun()
{
    // The following code will be executed by non event dispatching thread.
    final int choice;
    SwingUtilities.invokeAndWait(new Runnable() {

        @Override
        public void run() {
            // Error.
            choice = JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
        }            
    });
    return choice;
}

Of course this won't work as choice is final, and I cannot assign the returned value from dialog to it.

What is the correct way to achieve the above 3 objectives?

Was it helpful?

Solution

Have you tried:

public int fun()
{
    // The following code will be executed by non event dispatching thread.
    final int[] choice = new int[1];
    SwingUtilities.invokeAndWait(new Runnable() {
        @Override
        public void run() {
            // Error.
            choice[0] = JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
        }            
    });
    return choice[0];
}

OTHER TIPS

public int fun() throws InterruptedException, InvocationTargetException {
    // The following code will be executed by non event dispatching thread.
    ChoiceRunnable runabble = new ChoiceRunnable();
    SwingUtilities.invokeAndWait(runabble);

    return runabble.choice;
  }

  class ChoiceRunnable implements Runnable {
    private int choice;

    public void run() {
      choice = JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
    }
  }

Contrary on the popular belief you dont need to dispatch to AWT (EventQueue) thread to show the dialog. So just show it.

When you do JOptionPane,showMessge() your thread (Thread.currentThread()) is going to do wait(), and the dialog will pop up. Use the result after showMessage and you're good to go.

Thus:
choice = JOptionPane.showConfirmDialog(this, message, title, JOptionPane.YES_NO_OPTION);

May be I do not understand the question, but I do not get the answers either... if you want the calling thread to block on the call to fun(), why display the JOptionPane in a new (parallel) Thread? Shouldn't this be sufficient?

public int fun() {
    return JOptionPane.showConfirmDialog(null, message, title, JOptionPane.YES_NO_OPTION);
} 

PS How do you define a non event dispatching thread?

I am a bit tired of people saying things are one way or another but without actually knowing what they are talking about. I came here wondering which thread JOptionPane should be run on but I get conflicting answers with no real evidence to support either point. Well, I researched it myself and I am well satisfied with this answer so I will share.

A call to one of JOptionPane's showXXXDialog() is BLOCKING until the user selects ok/cancel/etc. In general you do not put such slow blocking insturctions on the Event Dispatch Thread (EDT) as a rule because every single other GUI component will freeze. So, a gut instinct to not put it on the EDT is good, but it is also wrong. The reason is as stated by some others, the method creates GUI components and this should always be done on the EDT. But what about the blocking? You will notice that even if you do run it on the EDT, it works fine. The reason is found inside the source code. The JOptionPane class creates a Dialog object and then calls show() followed by dispose(), the first of which is what blocks the thread. If you read the comments (or javadoc), you will see that it says this about the method:

If the dialog is modal and is not already visible, this call will not return until the dialog is hidden by calling hide or dispose. It is permissible to show modal dialogs from the event dispatching thread because the toolkit will ensure that another event pump runs while the one which invoked this method is blocked.

So, it is perfectly safe to run JOptionPane on the EDT despite it blocking. Obviously, it is safe to call Dialog's show() method off the EDT but the same is not true for JOptionPane because its methods are creating GUI components, adding listeners, accessing other containers when modal and blocking input to them, etc. You do not want all of this done off the EDT because it is not thread-safe and there could be problems. Admittedly, I have never seen problems when using JOptionPane off the EDT and so the chances seem low, but they are most certainly possible. Passing in a null for the container of the dialog and only giving immutable objects (like Strings) as arguments to the fields will significantly reduce (maybe even eliminate as far as I know) the chance of something bad happening because all relevant GUI components are made and accessed within the same thread while they are not visible. But, you should just be safe and put it on the EDT. It is not that difficult to call SwingUtilities.invokeAndWait().

Launch the JOptionPane from the EDT, and pass the value back with a FutureTask.

public int fun()
{
    FutureTask<Integer> dialogTask = new FutureTask<Integer>(new Callable<Integer>() 
    {
        @Override public Integer call() 
        {
            return JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
        }
    });
    SwingUtilities.invokeLater(dialogTask);
    int choice  = dialogTask.get();
}

Credit to jtahlborn How to pass results from EDT back to a different thread?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top