4

In my Java application with a Swing GUI, I would like to achieve the following.

There is a non-GUI thread running, performing some work. At one point, this thread needs input from the user before it can continue. Then, I would like to make some changes to the GUI, await a specific GUI action (like the user pressing the OK button), get the entered data from the GUI to the non-GUI thread, and let it continue with the computation.

Looking around, I have found a lot of information about how to initiate the execution of a (long running) task from the Swing GUI thread on another thread, but nothing on my problem.

SwingUtilites.invokeAndWait sounds like it does the job, but first, it takes a Runnable argument instead of a Callable, so there is no straightforward way to return a result, and second, it does not solve the problem of waiting for a certain GUI event.

I realize I could make up my own solution using e.g. a CountDownLatch, but to me, the problem seems frequent enough for there to be a standard solution.

So, my questions are: Is this really a frequent problem, and if yes, is there a solution in the standard library / libraries? If there is no standard solution, how would you solve it? If this problem doesn't occur often, why not?

mKorbel
  • 109,525
  • 20
  • 134
  • 319
sandris
  • 1,363
  • 13
  • 27
  • See similar question http://stackoverflow.com/questions/3981924/how-to-retrieve-a-value-that-must-be-computed-on-another-thread – Matt McHenry Feb 19 '14 at 01:49

4 Answers4

6

Kicking off the GUI changes is easy, so I assume you're only asking about getting data back to the worker thread.

First, create a Blocking Queue. Have the worker thread call take() on the queue, and it will block. In GUI space, once the user enters valid input, put it on the queue with offer() and the worker thread will receive the data and can continue.

BillRobertson42
  • 12,602
  • 4
  • 40
  • 57
  • 1
    +1 for using a higher-level abstraction, rather than explicit locks. It is *way way way* easier to get the code right (without subtle bugs) if you handle multi-threading this way. – Alex D Feb 22 '12 at 18:24
1

I think, you can use ExecutorService where you can also track progress of your task through Future interface.

JProgrammer
  • 1,135
  • 1
  • 10
  • 27
1

java.awt.EventQueue.invokeLater works nicely for running code on the AWT EDT. Propbably best to copy mutable data or better use immutable data. Locks are possible, but a bit dicey.

If you other thread is an event dispatch loop, you could implement something like invokeLater for your thread (but don't make it static!). Probably use it behind some interface that makes sense to the behaviour of the thread - so it's real operations rather than run which is specified as doing anything it pleases. If your thread is going to block, then a BlockQueue is fine, but don't block from the AWT EDT.

java.awt.EventQueue.invokeAndWait is like using a lock. Probably you are going to use another lock. Or perhaps a lock like invokeAndWait on you own thread. If you don't, AWT uses a lock anyway. So, uncontrolled nested locks, that probably means deadlock. Don't use invokeAndWait!

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • So are you basically saying that there is no ready-made solution, and using `SwingUtilities.invokeLater` in combination with my own lock is the only way to go? – sandris Feb 22 '12 at 16:06
0
final bool result = doSomething();
SwingUtilities.invokeLater( new Runnable(){
       //Runnable method implementation. 
       //use result in your method like local var.
    });

Make sure that your shared data is synchronized use lock objects. If you need to pass arguments to Runnable just make your local variables final, and use them in run method.

AlexTheo
  • 4,004
  • 1
  • 21
  • 35
  • I don't want to pass arguments to the `Runnable`, I'd like to get results back from there. I understand it's possible to achieve this via mutating thread safe objects, but it would be cleaner to get a return value from a method. – sandris Feb 22 '12 at 16:09
  • Then just make an class member and change it in your runnable object. The only thing you need is to synchronize this object. – AlexTheo Feb 22 '12 at 19:19