I rarely write multithreaded code, and am on shaky ground when doing so. I make mistakes. I would like a sanity check of something I am going to introduce in one of my apps.
There will be exactly one running instance of the Thread
class below in my application. (It will be stored in a servlet context attribute.) "Clients" will call one of three methods on the instance, getRunList()
to get data from it, signalFreshRunListCreation()
to tell the thread to generate new data, and stopThread()
to signal the thread to exit. On its own, the thread creates new data every 30 seconds.
Have I made any errors?
public class ThreadRunListDefault extends Thread {
// This set to true signals that the thread should exit.
private boolean _boStop = false;
// The thread sets this to true just before it sleeps.
private boolean _boIsSleeping = false;
// An external client sets this to true to signal that a new run list should be created.
private boolean _boFreshRunListRequested = false;
// This is a mutex to lock the run list while a fresh one is being created.
private Object _lockRunList = new Object();
// This is the actual data that will be returned.
private List<BeanRunOutput> _runList;
// This is the time the thread will sleep between refreshing the data on its own
private long _iSleepMilliseconds = 30000;
@Override
public void run() {
// Loop as long as no client has indicated the thread should stop.
while (!_boStop) {
// Create the data.
this.createFreshRunList();
// Only sleep if a refresh of the data wasn't requested while we were
// creating a fresh run list.
if (!_boFreshRunListRequested) {
try {
// We set the sleeping flag to true so that signalFreshRunListCreation() will
// know whether or not to call interrupt() on the thread and wake it up.
_boIsSleeping = true;
Thread.sleep(_iSleepMilliseconds);
} catch (InterruptedException e) { }
_boIsSleeping = false;
}
}
}
/*
* This is the method that will be called by run() to create new data in the _runList member variable.
*/
private void createFreshRunList() {
synchronized(this._lockRunList) {
// Here we put the code that creates the run list.
// Typically it will take 5 to 10 seconds.
_runList = new ArrayList<BeanRunOutput>();
}
}
/*
* This is the method a client will call to get the data in _runList.
*/
public List<BeanRunOutput> getRunList() {
synchronized(this._lockRunList) {
return _runList;
}
}
/*
* This is the method a client will call to signal the thread that a new run list should be created.
* If run() is currently gathering data (_boIsSleeping is false), a flag will be set to tell run()
* to run again immediately after it is finished, without sleeping first. If the thread is currently
* sleeping, it will be interrupted.
*/
public void signalFreshRunListCreation() {
if (_boIsSleeping) {
// Thread is sleeping so interrupt it.
this.interrupt();
} else {
// The thread is running so signal it should just run again when
// complete (in other words not sleep).
_boFreshRunListRequested = true;
}
}
/*
* This method allows a client to signal the thread to exit.
*/
public void stopThread() {
_boStop = true;
}
}