From: Joshua Cranmer on
On 07/12/2010 08:59 PM, markspace wrote:
> 1. Construct the first part of your GUI on the EDT as shown in the
> example, up to where you append "Begin Counting" to the text area.

Applet::init should be called from the EDT, if I understand my applets
correctly (I don't play around enough with them to say for sure).

> (I realize JTextArea.append() is thread safe, just in general you want
> to know how to execute code on the EDT after completing some backgound
> task.)

Mercifully, a lot of the document modelling stuff behind text areas is
actually thread-safe. Unfortunately, most of Swing is not so nice to you.

--
Beware of bugs in the above code; I have only proved it correct, not
tried it. -- Donald E. Knuth
From: Sal on
On Jul 12, 5:59 pm, markspace <nos...(a)nowhere.com> wrote:
> Sal wrote:
> >    public void init() {
> >       Container cp = getContentPane();
> >       cp.setBackground( darkGreen );
> >       cp.setLayout( new FlowLayout() );
> >       textArea = new JTextArea(30, 40);
> >       scrollPane = new JScrollPane();
> >       cp.add ( new JScrollPane( textArea ) );
> >       textArea.setEditable( false );
> >       textArea.append( "Begin counting " + nbrOfTimes +" \n" );  //
> > <------ look here
> >       int ctr = 0;
> >       while( ++ctr < nbrOfTimes );
> >       textArea.append( "End counting " + ctr +" \n" );
>
> Swing code like that above must be executed on the EDT, or it ain't
> going to work.  Building on what Jeff Higgins wrote, here's a direct
> link to an example which builds a very simple GUI on the EDT:
>
> <http://download.oracle.com/docs/cd/E17409_01/javase/tutorial/deployme...>
>
> You should study that, the rest of the section there on "Applets", and
> read through the Concurrency in Swing tutorial:
>
> <http://download.oracle.com/docs/cd/E17409_01/javase/tutorial/uiswing/...>
>
> It's a pain in the rear but there isn't any other way.  You're totally
> stuck until you understand this stuff;  we can't write ALL your code for
> you.  Once you get through with that, here's what I think a basic plan
> would be:
>
> 1. Construct the first part of your GUI on the EDT as shown in the
> example, up to where you append "Begin Counting" to the text area.
>
> 2. Find the "long task" part of your GUI; in your example it's that
> while loop.  That needs to be done OFF the GUI thread or it's going to
> block all updates, including displaying the part of the GUI you just
> made in #1.
>
> 3. Then, when #2 is done, display your final update(s) on the EDT.
>
> Use a SwingWorker for the long task, it's the easiest way.  It'll
> execute long tasks "in the background" more or less automatically for
> you, then run a foreground process on the EDT when it's done.  Something
> like:
>
> import javax.swing.JTextArea;
> import javax.swing.SwingWorker;
>
> public class MyWorker extends SwingWorker {
>     JTextArea textArea;
>     int ctr;
>     @Override
>     protected Object doInBackground()
>             throws Exception
>     {
>        // your long task here
>        while( ++ctr < 1000000 );
>        return null;
>     }
>
>     @Override
>     protected void done() {
>        textArea.append( "End counting " + ctr +" \n" );
>     }
>
> }
>
> That should give you enough to get something done on your own, although
> you've got a fair amount of reading to do.  Once you get that done and
> write the improved version of you code, let us know how you are faring.
>
> (I realize JTextArea.append() is thread safe, just in general you want
> to know how to execute code on the EDT after completing some backgound
> task.)

Thank you markspace for pointing me in the right direction. That's
what I needed to know to get going.
From: markspace on
Joshua Cranmer wrote:
> On 07/12/2010 08:59 PM, markspace wrote:
>> 1. Construct the first part of your GUI on the EDT as shown in the
>> example, up to where you append "Begin Counting" to the text area.
>
> Applet::init should be called from the EDT, if I understand my applets
> correctly (I don't play around enough with them to say for sure).


I think it's the opposite, although I could be wrong about that.
init(), start() etc. are likely to be called on the thread used by the
browser itself, not the EDT. That's way a bad applet could halt
rendering and make a browser unresponsive. init() is declared in
java.awt.Applet, not JApplet, and so should be thread safe. There's no
reason to call its methods on the EDT.

If you do something that's not thread safe in init() (like constructing
Swing components), it's up to you to make it thread safe.


>> (I realize JTextArea.append() is thread safe, just in general you want
>> to know how to execute code on the EDT after completing some backgound
>> task.)
>
> Mercifully, a lot of the document modelling stuff behind text areas is
> actually thread-safe. Unfortunately, most of Swing is not so nice to you.


I was pretty sure that only setText() and append() are thread safe for
JTextArea. Everything else, even getText() is not.
From: Lew on
markspace wrote:
> import javax.swing.JTextArea;
> import javax.swing.SwingWorker;
>
> public class MyWorker extends SwingWorker {
> JTextArea textArea;
> int ctr;
> @Override
> protected Object doInBackground()
> throws Exception
> {
> // your long task here
> while( ++ctr < 1000000 );
> return null;
> }
>
> @Override
> protected void done() {
> textArea.append( "End counting " + ctr +" \n" );
> }
> }

Shouldn't 'ctr' be at least 'volatile'?

--
Lew
From: markspace on
Lew wrote:

> Shouldn't 'ctr' be at least 'volatile'?


Yes, I believe so. I thought that SwingWorker made some memory
consistency guarantees regarding its methods, but I don't see anything
like that documented.

As a practical matter, I don't see how an object could execute on two
different threads without synchronization. doInBackground() uses
Executor.execute(), and that definitely creates a happens-before
relationship. done() uses a Future object's done() method, and submits
the SwingWorker's to the EDT via invokeLater. This is more murky to me,
I don't see any explicit happens-before synchronization point on that
code path.

And of course if it's not documented in the API, it could change at any
time. It still would be useful imo to make an explicit happens-before
relationship there. Any real code is going to need one, why make the
end-user implement that part each time?