5

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?

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875

6 Answers6

5

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];
}
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875
Rekin
  • 9,731
  • 2
  • 24
  • 38
3

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().

David
  • 111
  • 1
  • 1
2
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);
    }
  }
lweller
  • 11,077
  • 3
  • 34
  • 38
2

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

Nate W.
  • 9,141
  • 6
  • 43
  • 65
bestsss
  • 11,796
  • 3
  • 53
  • 63
  • But, is it safe to call JOptionPane from user thread? http://stackoverflow.com/questions/1595744/is-joptionpane-showmessagedialog-thread-safe – Cheok Yan Cheng Jan 21 '11 at 00:38
  • Well, that can be potentially unsafe to put it other words: you share no object whatsoever. AWT (the dialog) itself is thread safe. The execution, events, buttons, etc, is performed in the AWT thread. Like I've told already, the current thread blocks until the dialog is closed (closing the dialog and the following notify(), ensures all the memory fences you might need) – bestsss Jan 21 '11 at 00:51
  • I replied there as well. Basically if you try to modify the dialog outside the AWT (EventQueue) thread then you are potentially runnig into a race, also modifying quite a bit of AWT provided stuff is thread safe. Note, there can be only one active AWT thread, although there could be more than a single EventQueue thread running. – bestsss Jan 21 '11 at 00:55
  • Do you have any concrete reference for your statement? As most Oracle Java example is that, when they try to show an GUI dialog, they will have it on GUI dispatch thread. Another 3rd parties code example do the same as well : http://www.java2s.com/Code/Java/Threads/SwingUtilitiesinvokeLaterandswingthread.htm – Cheok Yan Cheng Jan 21 '11 at 16:46
  • I tried this on 32-bit HotSpot 7 Update 17 on 64-bit Windows 7. The dialog box outside frame appears but the inside isn't painted until the GUI thread responds to the paint message. Since the GUI thread has to be involved, there is no advantage to not using invokeAndWait(). – Nathan Apr 03 '13 at 23:57
  • @Nathan, involving the context AWT thread(s) is given - there is no chance to run the UI outside (esp swing). The advantage - the code is simpler and requires no additional class to be loaded. The disadvantage - some UIResources static factories are badly coded, for instance Border.createEtchedBorder may return race object. The code is still racy in multi-AppContext, though. Overall swing has quite *a lot* of races on static resources. – bestsss Apr 04 '13 at 09:58
1

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?

Manidip Sengupta
  • 3,573
  • 5
  • 25
  • 27
  • having null, as component is hard the correct one :) also it's correct by chance – bestsss Jan 20 '11 at 21:07
  • @bestsss: Providing `null` for the `Component` argument is perfectly okay; in this case, a default `Frame` is used. See http://download.oracle.com/javase/6/docs/api/javax/swing/JOptionPane.html – Nate W. Jan 20 '11 at 22:54
  • @Shakedown: I know that very well, but modality may not suffice what you need (The JFrame is the same if you dont provide Window for creating a new JDialog(), which while inaccessible under normal conditions [SwingUtilities.getSharedOwnerFrame() is package private] new JDialog().getOwner() returns it). Also that will place the dialog on the default screen w/ multi-screen setup. See, not such a rookie as expected. – bestsss Jan 21 '11 at 00:09
  • On a side: I quite rarely read the javadoc and just go for the source code. It's not a practice I'd recommend but at least I am sure what I opt for – bestsss Jan 21 '11 at 00:13
  • @Manidip Sengupta But, is it safe to call JOptionPane from user thread? http://stackoverflow.com/questions/1595744/is-joptionpane-showmessagedialog-thread-safe – Cheok Yan Cheng Jan 21 '11 at 00:38
  • @Yan Cheng CHEOK I have not run into an unsafe situation yet - in fact, I do other fancy threaded stuff in the message part, like having a spinning bar while something is being downloaded, and the user can abort the download by clicking on "No" or "Cancel" - works so far for me. – Manidip Sengupta Jan 21 '11 at 04:09
0

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?

birkner
  • 133
  • 9