Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwingUtilites: how to return values from another thread in Java?

I am trying to make an application in Java. To make Swing work correctly, I did this:

public static void main(String[] array){ 

String outerInput;
SwingUtilities.invokeLater(new Runnable(){
    @Override
    public void run() {
        // I want this string input.
        String input = JOptionPane.showInputDialog(
            null,"Stop ?", JOptionPane.QUESTION_MESSAGE);  
});
// How can I get this input value in String outerInput?
}

How would I get this input string in my main body?

like image 516
Ashish Negi Avatar asked Sep 14 '11 15:09

Ashish Negi


People also ask

How do you call a thread from another thread in Java?

There are two ways to run code in a new thread in Java — one by extending the Thread class and overriding the run() method, the other one by implementing the Runnable interface and then passing an instance to a Thread constructor.

What is SwingUtilities invokeLater() used for?

An invokeLater() method is a static method of the SwingUtilities class and it can be used to perform a task asynchronously in the AWT Event dispatcher thread. The SwingUtilities. invokeLater() method works like SwingUtilities. invokeAndWait() except that it puts the request on the event queue and returns immediately.

What is Java SwingUtilities?

Stores the position and size of the inner painting area of the specified component in r and returns r .


2 Answers

You can use AtomicReference<String> for passing values between threads in a thread-safe manner.

As noted by Hemal, you'll need some synchronization between two threads to make sure it was already executed. For example, you can use CountDownLatch or use SwingUtilities.invokeAndWait (make sure you don't call it from Swing thread!)

Update: here is the complete example using AtomicReference and CountDownLatch

public class Main {
    public static void main(String[] args) throws InterruptedException {
        final AtomicReference<String> result = new AtomicReference<String>();
        final CountDownLatch latch = new CountDownLatch(1);

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                String input = JOptionPane.showInputDialog(null, "Stop?", "Stop?", JOptionPane.QUESTION_MESSAGE);
                result.set(input);

                // Signal main thread that we're done and result is set.
                // Note that this doesn't block. We never call blocking methods
                // from Swing Thread!
                latch.countDown();
            }
        });

        // Here we need to wait until result is set. For demonstration purposes,
        // we use latch in this code. Using SwingUtilities.invokeAndWait() would
        // be slightly better in this case.

        latch.await();

        System.out.println(result.get());
    }
}

Also read this answer about general design of GUI (and Swing) applications.

like image 168
Peter Štibraný Avatar answered Sep 28 '22 03:09

Peter Štibraný


How would I get this input string in my main body?

You wouldn't. The idea that your "main" would invoke a Swing dialog box and then do something with the results is contrary to the entire idea of a graphical user interface.

In a GUI, you design your program to deal with a series of user-initiated events. Those events may be completely asynchronous, such as the keystrokes, selection, and menu choices of your typical word processor. Or they may be scripted, such as the question-answer format of a "wizard."

Assuming that you want to do something like the latter, you would implement it using the following sequence:

  1. The user initiates some action, perhaps selecting a menu choice. This is turned into an invocation of an ActionListener, which decides that it needs more input from the user.
  2. The ActionListener, which is executed on the event dispatch thread, is permitted to do anything that it wants to the UI, such as displaying a dialog. That dialog may be modal or non-modal; in one case the output is available to the original listener, in the other you need to write a new listener to take subsequent action.
  3. Once you have enough information, you may choose to invoke a background operation. You would typically have a thread-pool to service these requests. You would not attempt to perform the request on the "main" thread; in fact, for all intents the main thread is no longer running.
  4. When your operation completes running, it would push data back to the event dispatch thread using SwingUtilities.invokeLater(). While you could use invokeAndWait() to send results to Swing in the middle of your background operation, that's rarely a good idea. Instead, create a sequence of operations, preferably one that is easily canceled by the user.

The "standard" way to initiate operations on a background thread is via SwingWorker. There are alternatives; for example, you could use a BlockingQueue to send operations to a single long-running background thread, and use invokeLater() to return the results.

Regardless, there's one rule that you do not want to break: never, ever, perform a blocking operation on the event dispatch thread. If you do that, then your application is broken.

like image 43
parsifal Avatar answered Sep 28 '22 04:09

parsifal