🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: borland.public.jbuilder.java.language
Article #14419

Re: Java Multithreading

#14419
From: Tad Frysinger
Date: Tue, 07 Sep 1999 00:00
232 lines
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-mail
References: <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