Article View: borland.public.jbuilder.java.language
Article #14419Re: Java Multithreading
From: Tad Frysinger
Date: Tue, 07 Sep 1999 00:00
Date: Tue, 07 Sep 1999 00:00
232 lines
7845 bytes
7845 bytes
In article <7q49s5$p6m12@forums.borland.com>, kevind@saccapital.com
says...
> I need to be able to catch when a thread has finished executing.
>
> I have a frame which starts two threads, then control returns to the frame.
> These threads move/message data from one database to another.
>
> From within the frame how can I capture the fact that a thread has died,
> most likely due to an error.
>
> Thanks Much
>
> KD
>
>
>
This is a question I see up here a lot, so I am going to go into some
length for the "answer" (at least, my answer <g>). 'Catching whether a
thread died' is akin to knowing the status at all times of what is going
on in the thread, knowing when it finishes ok, vs. some other type of
condition. Here is a thorough example of how you do this.
Check out the SwingWorker.java classes and discussions in the archives at
www.theswingconnection.com. Using that class as a base, here is what I
typically do - I write my method that actually does the work, and wrap it
in a SwingWorker instance. (note the construction of SwingWorker, how I
override the appropriate methods to get the behavior I want, and that it
wraps the method that actually does the work). I also make sure the user
has feedback both in terms of messages, and a progress bar. These must
be done in a threadsafe manner. Finally, I provide a way for the user to
interrupt the process. All messaging is done via a custom 'MyEvent'
class that has static vars for ID types. Note the 'processor' variable.
Assuming your threaded process starts based on some GUI event (button
press):
void myButton_actionPerformed(ActionEvent e )
{
try
{
// Notify my GUI of what is about to happen. This must be done
// in a threadsafe manner!
MyEvent ev =
new MyEvent( this,
MYEvent.PROCESS_STARTED,
"Starting my lengthy process..." );
processMyEvent( ev );
// update progress bar
progress.setValue( 0 );
progress.setMaximum( tableNames.size() );
// Do the work by spawning a worker thread
processor = new SwingWorker()
{
// Override the construct() method in SwingWorker, and it
// will return a MapperEvent which is what is returned
// by the get() call in the finished() method.
public Object construct()
{
bIsCopying = true;
return doWork();
}
// Override the finished() method to update the UI.
// Since the thread is done here, UI update is in the
// Event thread, and thus is thread-safe
public void finished()
{
// Do any clean up here...
// Determine how we finished. doWork() has either
// indicated interruption, normal end, or process
// exception end.
processMyEvent( (MyEvent)get() );
}
};
}
catch (Exception ex )
{
stopProcessor();
processMapperEvent(
new MapperEvent( this,
MapperEvent.EXCEPTION_EVENT,
ex ) );
}
}
Object doWork()
{
try
{
// Do the time consuming work here
.
.
.
// Useful for debugging database work
java.sql.DriverManager.setLogStream(System.out);
/// threadsafe updating of GUI
safeSendEvent(
new MyEvent(this,
MyEvent.MESSAGE_EVENT,
"My custom update message for user"));
// Update the status
safeUpdateProgress( i + 1 );
// Normal done
return new MyEvent( this,
MyEvent.PROCESS_COMPLETED,
null );
}
catch (InterruptedException e )
{
// This is thrown when a process calls the .interrupt method on
// the processor thread, or when the above process recognizes it
// has been interrupted.
try
{
// do any exception recovery, rollbacks, etc. here
}
catch (Exception ex )
{
}
return new MyEvent( this,
MyEvent.PROCESS_CANCELLED,
e );
}
catch (Exception ex )
{
try
{
// Other generic exception handling here
}
catch (Exception exr )
{
}
return new MyEvent( this,
MyEvent.PROCESS_EXCEPTION,
ex );
}
finally
{
// Update the progress bar to have 0 progress
safeUpdateProgress(0);
java.sql.DriverManager.setLogStream(null);
}
}
void safeUpdateProgress( final int i )
{
// Update progress bar,
Runnable setProgressBar = new Runnable()
{
public void run()
{
progress.setValue(i);
}
};
SwingUtilities.invokeLater(setProgressBar);
}
void safeSendEvent( final MapperEvent e )
{
// Send a message
Runnable sendMessage = new Runnable()
{
public void run()
{
processMyEvent(e);
}
};
SwingUtilities.invokeLater(sendMessage);
}
protected void processMyEvent(MapperEvent e)
{
for (int i=0; i<mapperEventListeners.size(); i++)
{
((MyEventListener)myEventListeners.elementAt(i)).
handleMyEvent(e);
}
}
// from my GUI:
public void handleMyEvent( MyEvent e )
{
switch (e.getID() )
{
case My.PROCESS_STARTED :
allowChanges( false );
resultTab.setSelectedIndex( MSG_PANEL );
taMessage.setText("");
errorPanel.clearExceptions();
statusBar.setText( (String)e.getUserObject() );
taMessage.append( statusBar.getText() + "\n" );
break;
case MapperEvent.PROCESS_COMPLETED :
entityTree.clearSelectedTables();
allowChanges( true );
statusBar.setText("Copying completed successfully");
taMessage.append( statusBar.getText() + "\n" );
errorPanel.clearExceptions();
break;
etc.
}
statusBar.paintImmediately( statusBar.getVisibleRect() );
msgPane.paintImmediately( msgPane.getVisibleRect() );
}
And if you want the user to be able to interrupt the process via another
button, have this in the handler for that button (note that the
'processor' variable was initialized above).
/**
Provide a way for this class to interrupt the worker. Remember that
the worker wraps a thread - .interrupt here is doing more work
then simply Thread.interrupt(), which doesn't actually interrupt
anything! See SwingWorker.java for details.
*/
if ( processor != null )
{
processor.interrupt();
processor = null;
}
Tad Frysinger (TeamB)
Technical Details
Message-ID:
<MPG.123ed43351af843298969e@forums.borland.com>Path:
arc1-ca.pugleaf.net!unknown!not-for-mailReferences:
<7q49s5$p6m12@forums.borland.com>Show Full Headers
Complete Article Headers
X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: f74b3,513bff9c58505db2 X-Google-Attributes: gidf74b3,public From: tfrysinger@earthlink.net (Tad Frysinger) Subject: Re: Java Multithreading Date: 1999/09/07 Message-ID: <MPG.123ed43351af843298969e@forums.borland.com> X-Deja-AN: 522122919 References: <7q49s5$p6m12@forums.borland.com> Organization: RTOne Newsgroups: borland.public.jbuilder.java.language