I don't have a lot to say, but this is my little bit.

Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Wednesday, September 16, 2009

EnhancedThreadExample


I wrote a little application to show off why EnhancedThread is useful. This app has a button which, when clicked, creates a thread which does a trivial task a lot of times. The thread is monitored by a listener, which updates a progress bar, which is visible in a scroll pane in the window. When the thread completes its task, the system beeps and the progress bar is removed from the window.

Download the application here

Download the XCode project (source code) here

This application is also a visual way to demonstrate that threads do not always execute their tasks with the same speed. Even though all the threads in EnhancedThreadExample are performing exactly the same task, you can see in the picture that some threads perform faster than the thread before or after.

To run this application, download it and double click on the EnhancedThreadExample.jar file, or from the command line issue 'java EnhancedThreadExample.jar'.

In years past this application would have been a test case for a memory leak in Apple's implementation of the Swing classes. They must have fixed the bug, but previously the JProgressBar's UI class would fail to clean up its pointer to the native progress-bar UI, the one that makes the pleasant animated flowing blue bar. The result was that every new progress bar displayed leaked a little memory -- a substantial amount, actually. Over time, an application which used progress bars would slow down then run out of memory. I used a memory mapping tool to watch as this application executes, and there was no noticeable memory leak, even after using hundreds of progress bars. I bet it was no more than one line of missing code on Apple's part.

Tuesday, September 15, 2009

EnhancedThread makes Java threads more useful


A while back I wrote an application which spawned threads for lengthy import and export operations. I wanted to follow the thread progress using a popup progress bar. I wrote EnhancedThread to help make this possible and easy and generalized.

The most important addition is that a thread's state can be monitored by a listener. An EnhancedThread has the concept of progress, being stopped (paused), and being canceled. A thread also carries a mutable message. These features specifically make it easy to develop a GUI which displays the thread's progress, allows the user to pause, resume and cancel the task, displays messages from the thread to the user.

So in my case, I used an EnhancedThread to import collections of objects, which could take between, say, ten seconds and ten minutes. Along the way, I wanted to display a message indicating what object was currently being imported. Since several import threads could be running concurrently, I wanted to be able to pause, resume, and cancel them, so the user could prioritize their lengthy tasks.

Furthermore, I wanted to have a single window which displayed all threads, but I wanted that window to be monitor my application's master thread pool. The user only needed to see some threads, so EnhancedThread can indicate whether the thread is a user-visible thread, and can also explicitly signal the GUI that the thread manager window should be popped up for this thread. (Of course, that was only a flag, which the GUI could choose to ignore as appropriate.)

import java.util.EventListener;

import javax.swing.*;
import javax.swing.event.*;

/**
*
*
* @author Nicholas R. Rinard
*/
public class EnhancedThread extends Thread {

// ---- v a r i a b l e d e c l a r a t i o n s ---------------------- //

public static final String messageProperty = "__message_property__";
public static final String progressProperty = "__progress_property__";

private String message = "";
private double progress = 0; // negative indicates indeterminate

private boolean canceledFlag = false;
private boolean stoppedFlag = false;

// these public flags (no getters/setters) are clues to the GUI
public boolean isUserVisibleThread = true;
public boolean threadRequestsVisibleWindow = false;
public boolean threadCanBeCanceled = true;

private final EventListenerList allListeners = new EventListenerList();

// ---- p u b l i c a p i -------------------------------------------- //

/**
*
*/
public EnhancedThread( String threadName, double threadProgress, String threadMessage ) {
super( threadName );
this.progress = threadProgress;
this.message = threadMessage;
}

public void addChangeListener( ChangeListener l ) {
if( l != null ) {
allListeners.remove( ChangeListener.class, l );
allListeners.add( ChangeListener.class, l );
}
}

public double getProgress() {
return this.progress;
}

public void setProgress( double newProgress ) {
if( newProgress == this.progress ) return;
this.progress = newProgress;
fireStateChanged();
}

public String getMessage() {
return this.message;
}

public void setMessage( String newMessage ) {
if( newMessage.equals( this.message ) ) return;
this.message = newMessage;
fireStateChanged();
}

public void setCanceledFlag( boolean isCanceled ) {
if( this.canceledFlag == isCanceled ) return;
this.canceledFlag = isCanceled;
fireStateChanged();
}

public boolean getCanceledFlag() {
return this.canceledFlag;
}

public void setStoppedFlag( boolean isStopped ) {
if( this.stoppedFlag == isStopped ) return;
this.stoppedFlag = isStopped;
fireStateChanged();
}

public boolean getStoppedFlag() {
return this.stoppedFlag;
}

public String toString() {
return "Thread{name=" + this.getName() + "; progress=" + this.getProgress() +"; message=" + this.getMessage() + "}";
}

// ---- p r i v a t e c o n v e n i e n c e m e t h o d s ---------- //

private void fireStateChanged() {
EventListener [] listeners = this.allListeners.getListeners( ChangeListener.class );
for( int i = 0; i < listeners.length; i++ ) {
ChangeListener listener = (ChangeListener) listeners[ i ];
ChangeEvent e = new ChangeEvent( this );
listener.stateChanged( e );
}
}
}