2

I have a non-GUI thread that starts a JFrame using

java.awt.EventQueue.invokeLater(new Runnable() {

    public void run() {
        cardReadPunchGUI = new IBM1622GUI();  // instantiate
        cardReadPunchGUI.setVisible(true);
    }
});

Part of IBM1622GUI's constructor instantiates a "model" for itself, which my non-GUI thread needs access to:

cardReadPunch = IBM1622GUI.getModel();

What is the correct way for my non-GUI thread to synchronize with the new GUI that's been "invoked later"? (Without synchronization, of course, IBM1622GUI.getModel() just tends to return null.)

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Chap
  • 3,649
  • 2
  • 46
  • 84
  • It's right below it in the Javadoc. [invokeAndWait](http://download.oracle.com/javase/6/docs/api/java/awt/EventQueue.html#invokeAndWait(java.lang.Runnable)) –  Aug 03 '11 at 03:14

4 Answers4

4

Use

javax.swing.SwingUtilities.invokeAndWait(Runnable doRun);

instead.

Causes doRun.run() to be executed synchronously on the AWT event dispatching thread. This call blocks until all pending AWT events have been processed and (then) doRun.run() returns.

cgull
  • 1,407
  • 1
  • 11
  • 18
  • No this is really really bad. You should never use invokeAndWait(). it can lock up your entire program. Use invokeLater(). There really aren't any real cases where you should ever use invokeAndWait(). Read the docs they say the same thing I'm saying here. Avoid it. – chubbsondubs Aug 03 '11 at 03:12
  • 2
    @chubbard: That's an extremely non-context-sensitive thing to say. If it's a non-blocking operation `invokeAndWait()` is a perfectly safe thing to call, particularly when it's being called from a background thread to start with. If it's being called from a background thread, it literally cannot "lock up your entire program." – Mark Peters Aug 03 '11 at 03:15
  • @chubbard: All the Javadoc warns against is calling this method from the EDT. The OP has specifically said they are calling from a non-GUI thread. – cgull Aug 03 '11 at 03:24
  • I wouldn't get into a habit of using it. You don't need it. While this case might be ok to use it because it's not blocking what happens the next time when it's used inappropriately. Use invokeLater() and you're always safe. – chubbsondubs Aug 03 '11 at 03:26
  • 1
    @chubbard: Define "always safe". It seems like you're trying to boil down multithreaded GUIs into simple rules, but they're more complex than that. You need to actually understand what the concerns here are. There are plenty of times where `invokeLater()` is not "safe" since you need a happens-after relationship to exist. You get that in a completely safe manner with `invokeAndWait` but not with `invokeLater`. Plus, nothing that happens on the Swing thread should be blocking anyhow, so it really should be safe to use. Blanket statements have very little use in programming. – Mark Peters Aug 03 '11 at 03:47
  • @Mark, @chubbard: Okay - I was confused about when `invokeAndWait` actually returned. Since it most definitely *is* being issued from a background thread, I don't see any potential for deadlocking, and it certainly seems to block the issuing thread for exactly the right window of execution. `CountDownLatch()` is neat; gives finer-grained control and good to know about, but I think `invokeAndWait()` will do the trick. – Chap Aug 03 '11 at 03:57
  • There still is potential for locking up. If your background is holding a lock while using invokeAndWait(), and the UI tries to obtain that lock you can cause a lock up even by operating under normal conditions (i.e. using invokeAndWait() from the background thread). My point is get in the habit of using invokeLater and you can trade data without using locks and without the fear of deadlock. Try and impose synchronous code with threads and you'll get yourself into trouble (deadlock). – chubbsondubs Aug 03 '11 at 21:16
  • @Chubbard Not using a feature EVER, just because it is bad in SOME cases is idiotic and narrow minded. If a feature has a use SOMETIMES, it still has a use. Having an action happen only once the UI has been update is a perfect example of using InvokeAndWait(). In this case he should use InvokeAndWait() – Vort3x Aug 09 '11 at 00:50
2

Id suggest you share an CountDownLatch initialized to 1 with both both the non-GUI and GUI threads.

The non GUI thread when it starts will call latch.await() which will put it in a blocked state.

The GUI thread will call latch.countDown() when it finishes its initialization after which the non-GUI thread will exit from the await call and both threads are synchronized.

arun_suresh
  • 2,875
  • 20
  • 20
2

Well, if you have access to it you could always move that particular logic outside of the Swing thread and onto the thread that calls invokeLater. There's nothing unsafe about doing what you're doing there off of the Swing thread, assuming the constructor for IBM622GUI is well behaved.

Other than that, you could make use of various other mechanisms.

  1. You could use invokeAndWait, as cgull beat me to saying.
  2. You could have the runnable set the value of a Future instead of a direct reference, and block on the main thread by calling the future's get method.
  3. You could have a CountDownLatch with a starting count of 1 which you await() on your main thread, and countDown() from the Swing thread.

There are many, many utilities to help with synchronization.

Mark Peters
  • 80,126
  • 17
  • 159
  • 190
  • **There are many, many utilities to help with synchronization.** Yes, there certainly are. With all due respect to the Java designers, who are far more brilliant than I'll ever be, I have to wonder if Java's concurrency tools aren't just a little overwrought. – Chap Aug 03 '11 at 04:08
1

Typically you pass parameters to the Thread. Run the logic in the background. And then post back any modifications you need to do to any of those objects, or UI elements on the UI thread using SwingUtilities.invokeLater(). Typically I create a simple a utility that allows me to specify what should run on the background thread, and what should run on the UI thread. SwingWorker is something you could use although I find it extremely painful to use. Something simple like this:

new AsyncThread<Param,T>() {
   public T executeInBackground( Param param ) {
      // do something long running
      T result = // do something long running;
      return T;
   }

   public void executeOnUI( T result ) {
      // update the UI here, or modify the model, etc.
   }
}.execute( param );

AsyncThread would execute the executeInBackground() method on another thread. Then internally it would post back to UI thread using SwingUtilities.invokeLater(). Then executeOnUI would run on the UI thread. The execute() method could create a thread to run in background, handle exceptions, etc.

I'd let the GUI possibly kick off the thread, and let the GUI pass it's model, or whatever part it needs, to the thread. Instead of the other way around. That way you can have the UI give feedback about that background thread that's running. But, you can't let the background thread touch (write/modify/change) members of that model that the UI thread would be reading/writing too at the same time. So if you plan on modifying the model in response to the background thread, post it back to the UI thread to be safe.

chubbsondubs
  • 37,646
  • 24
  • 106
  • 138
  • 2
    1. I'm not sure why you find SwingWorker painful to use, it seems like you went out of your way to make a utility that does the same thing but which you'll now have to maintain. 2. You're missing the point that it's the **background** thread that needs the result of what the **UI** thread has done. – Mark Peters Aug 03 '11 at 03:17
  • It's just my preference to not use it. The early days of it were pretty awful. They have changed the interface somewhat, but I like to handle exceptions automatically, giving feedback automatically possibly putting up a spinner dialog, so it's fairly minimal to create new background jobs. I've found I can make a pretty tight class that makes doing background jobs a snap. SwingWorker requires a lot more code, but use it if you want. – chubbsondubs Aug 03 '11 at 03:31