bb.util
Class ThreadMonitor

java.lang.Object
  extended by bb.util.ThreadMonitor

public class ThreadMonitor
extends Object

Class which actively monitors the thread state of a JVM looking for issues. Reports events as they are detected to any registered ThreadMonitorListeners.

Monitoring is started by calling startMonitoring, is stopped by calling stopMonitoring, and isMonitoring reports if monitoring is currently active. It is legitimate to start and stop monitoring as often as desired.

There are certain situations where the code has to record information but no reference to an appropriate external handler class is available. A prime example is if during the monitoring or listener callback a Throwable is caught. Rather than write the information to a console that could disappear when the application quits, each instance of this class instead maintains a logger2 field where this information is written to. The current implementation logs to a file in the standard log directory.

This class is multithread safe: almost every method (including private ones) is synchronized. The sole exceptions are the private fireXXX methods. These methods are called internally by this class and interact with arbitrary external ThreadMonitorListener classes. As a strategy to prevent deadlock, it is critical that the thread which calls these fireXXX methods initially owns no locks. Thus, these methods must be unsynchronized, since otherwise the lock on this ThreadMonitor instance would be held.

Author:
Brent Boyer

Nested Class Summary
private  class ThreadMonitor.MonitorTask
          Class which detects thread issues and calls the appropriate fireXXX event notification method.
static class ThreadMonitor.UnitTest
          See the Overview page of the project's javadocs for a general description of this unit test class.
 
Field Summary
private  boolean deadlocked
          Records whether or not thread deadlock has been detected.
private  long instanceId
          This instance's Id.
private static AtomicLong instanceIdNext
          The next ThreadMonitor instance's instanceId field.
private  long interval
          Specifies how often the thread state should be checked.
private static long interval_default
          Default value for interval.
private  Set<ThreadMonitorListener> listeners
          Set of all ThreadMonitorListeners that are interested in thread events.
private  Logger2 logger2
          Logger where certain information (e.g. otherwise unhandleable errors) gets written to.
private  ThreadMonitor.MonitorTask monitorTask
          ThreadMonitor.MonitorTask used to measure the thread state.
private  int nextLoggerId
          The next logger2's Id.
private  int nextTimerId
          The next timer value's Id.
private  String state
          Last thread state that was detected.
private  Timer timer
           
 
Constructor Summary
ThreadMonitor()
          Simply calls this(new ThreadMeasurer.ThreadMeasurer(), interval_default).
ThreadMonitor(ThreadMeasurer measurer, long interval)
          Constructor.
 
Method Summary
 boolean addListener(ThreadMonitorListener listener)
          Attempts to add listener to an internal set.
private  void fireOnDeadlocked(String state)
           
private  void fireOnMonitoringError(Throwable t)
           
private  void fireOnMonitoringStarted()
           
private  void fireOnMonitoringStopped()
           
private  void fireOnNotDeadlocked(String state)
           
private  void fireOnThreadState(String state)
           
private  void flushLoggerIfCreated()
          Flushes logger2 if and only if it has been created.
private  boolean getDeadlocked()
           
private  ThreadMonitorListener[] getListeners()
          Returns a defensive copy of listeners for use by the fireXXX methods.
private  Logger2 getLogger2()
          Returns logger2, lazy initializing it if necessary.
private  String getLoggerSuffix()
          Returns a suffix to assign to logger2's filename whenever it is assigned.
 String getThreadState()
          Returns the thread state that was detected at the last check cycle.
private  String getTimerName()
          Returns a name to assign to timer whenever it is assigned.
 boolean isDeadlocked()
          Reports whether or not thread deadlock was detected at the last check cycle.
 boolean isMonitoring()
          Reports whether or not this instance is actively monitoring threads.
 boolean removeListener(ThreadMonitorListener listener)
          Attempts to remove listener from an internal set.
private  void setDeadlocked(boolean deadlocked)
           
private  void setThreadState(String state)
           
 boolean startMonitoring()
          Starts monitoring if it is currently not happening.
 boolean stopMonitoring()
          Stops monitoring if it is currently happening.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

interval_default

private static final long interval_default
Default value for interval.

The current value for this constant is 10 seconds. See benchmark_impactOfMonitoring for why it was determined that this value should not burden a JVM excessively.

See Also:
Constant Field Values

instanceIdNext

private static final AtomicLong instanceIdNext
The next ThreadMonitor instance's instanceId field.

Contract: is initialized to 0, and if this field has that value, it means that no instances have been created.


instanceId

private final long instanceId
This instance's Id.


nextTimerId

private int nextTimerId
The next timer value's Id.

Contract: is initialized to 1, and if this field has that value, it means that timer has never been assigned.


timer

private Timer timer

monitorTask

private final ThreadMonitor.MonitorTask monitorTask
ThreadMonitor.MonitorTask used to measure the thread state.

Contract: is never null.


interval

private final long interval
Specifies how often the thread state should be checked.

Units: milliseconds.

Contract: is > 0.


state

private String state
Last thread state that was detected. Will be null if no such state was ever detected (e.g. if monitoring was never started).


deadlocked

private boolean deadlocked
Records whether or not thread deadlock has been detected.


listeners

private final Set<ThreadMonitorListener> listeners
Set of all ThreadMonitorListeners that are interested in thread events.

Contract: is never null nor contains null elements.


nextLoggerId

private int nextLoggerId
The next logger2's Id.

Contract: is initialized to 1, and if this field has that value, it means that logger2 has never been assigned.


logger2

private Logger2 logger2
Logger where certain information (e.g. otherwise unhandleable errors) gets written to.

Note: this field is lazy initialized inside getLogger2 (because this field will never be used in well behaved applications), may be flushed in flushLoggerIfCreated (if previously initialized), and is closed and nulled out in stopMonitoring (if previously initialized). All other code should always use getLogger2 and never directly access this field.

Constructor Detail

ThreadMonitor

public ThreadMonitor()
Simply calls this(new ThreadMeasurer.ThreadMeasurer(), interval_default).


ThreadMonitor

public ThreadMonitor(ThreadMeasurer measurer,
                     long interval)
              throws IllegalArgumentException
Constructor.

Throws:
IllegalArgumentException - if measurer == null; interval <= 0
Method Detail

startMonitoring

public boolean startMonitoring()
Starts monitoring if it is currently not happening. Specifically, it will create a daemon Timer that executes monitorTask at a rate specified by interval.

This method may safely be called multiple times (if the first call succeeds, subsequent calls do nothing).

Returns:
true if this call actually started the monitoring, false if this instance was already monitoring when called

getTimerName

private String getTimerName()
Returns a name to assign to timer whenever it is assigned. Attempts to return a value that is both unique to this instance as well as unique to each of this instance's timer values.


isMonitoring

public boolean isMonitoring()
Reports whether or not this instance is actively monitoring threads.


stopMonitoring

public boolean stopMonitoring()
Stops monitoring if it is currently happening.

This method may safely be called multiple times (if the first call succeeds, subsequent calls do nothing).

Returns:
true if this call actually stopped the monitoring, false if this instance was not monitoring when called

getThreadState

public String getThreadState()
                      throws IllegalStateException
Returns the thread state that was detected at the last check cycle.

This method never causes a new thread state measurement to be made, so it executes extremely quickly, and may be called frequently with low impact on the JVM. On the other hand, the result may be obsolete, especially if this instance was constructed with a large value for interval.

Throws:
IllegalStateException - if instance is currently not monitoring (i.e. isMonitoring returns false)

setThreadState

private void setThreadState(String state)

isDeadlocked

public boolean isDeadlocked()
                     throws IllegalStateException
Reports whether or not thread deadlock was detected at the last check cycle.

This method never causes a new thread state measurement to be made, so it executes extremely quickly, and may be called frequently with low impact on the JVM. On the other hand, the result may be obsolete, especially if this instance was constructed with a large value for interval.

Throws:
IllegalStateException - if instance is currently not monitoring (i.e. isMonitoring returns false)

getDeadlocked

private boolean getDeadlocked()

setDeadlocked

private void setDeadlocked(boolean deadlocked)

getListeners

private ThreadMonitorListener[] getListeners()
Returns a defensive copy of listeners for use by the fireXXX methods. This is important both for multithread safety (see this class's javadocs) and for reasons explained in this forum posting.


addListener

public boolean addListener(ThreadMonitorListener listener)
                    throws IllegalArgumentException
Attempts to add listener to an internal set.

Returns:
true if listener was added by this call to the internal set, false if already present
Throws:
IllegalArgumentException - if listener == null

removeListener

public boolean removeListener(ThreadMonitorListener listener)
                       throws IllegalArgumentException
Attempts to remove listener from an internal set.

Returns:
true if listener was removed by this call from the internal set, false if already absent
Throws:
IllegalArgumentException - if listener == null

fireOnMonitoringStarted

private void fireOnMonitoringStarted()

fireOnMonitoringStopped

private void fireOnMonitoringStopped()

fireOnMonitoringError

private void fireOnMonitoringError(Throwable t)

fireOnThreadState

private void fireOnThreadState(String state)

fireOnDeadlocked

private void fireOnDeadlocked(String state)

fireOnNotDeadlocked

private void fireOnNotDeadlocked(String state)

getLogger2

private Logger2 getLogger2()
Returns logger2, lazy initializing it if necessary.


getLoggerSuffix

private String getLoggerSuffix()
Returns a suffix to assign to logger2's filename whenever it is assigned. Attempts to return a value that is both unique to this instance as well as unique to each of this instance's logger2 values.


flushLoggerIfCreated

private void flushLoggerIfCreated()
Flushes logger2 if and only if it has been created.