Java/Threads/Utilities
Содержание
- 1 Busy Flag
- 2 Early return
- 3 Exception call back
- 4 Finds a resource with the given name.
- 5 Listing all threads and threadgroups in the VM.
- 6 Return a new instance of the given class. Checks the ThreadContext classloader first, then uses the System classloader.
- 7 Sleep utilities
- 8 Thread-based logging utility
- 9 Transition Detector
- 10 View current Threads in a table
Busy Flag
/*
*
* Copyright (c) 1997-1999 Scott Oaks and Henry Wong. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and
* without fee is hereby granted.
*
* This sample source code is provided for example only,
* on an unsupported, as-is basis.
*
* AUTHOR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. AUTHOR SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*
* THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
* CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
* PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
* NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
* SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
* SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
* PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). AUTHOR
* SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
* HIGH RISK ACTIVITIES.
*/
public class BusyFlag {
protected Thread busyflag = null;
protected int busycount = 0;
public synchronized void getBusyFlag() {
while (tryGetBusyFlag() == false) {
try {
wait();
} catch (Exception e) {}
}
}
public synchronized boolean tryGetBusyFlag() {
if (busyflag == null) {
busyflag = Thread.currentThread();
busycount = 1;
return true;
}
if (busyflag == Thread.currentThread()) {
busycount++;
return true;
}
return false;
}
public synchronized void freeBusyFlag() {
if (getBusyFlagOwner() == Thread.currentThread()) {
busycount--;
if (busycount == 0) {
busyflag = null;
notify();
}
}
}
public synchronized Thread getBusyFlagOwner() {
return busyflag;
}
}
/*
*
* Copyright (c) 1997-1999 Scott Oaks and Henry Wong. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and
* without fee is hereby granted.
*
* This sample source code is provided for example only,
* on an unsupported, as-is basis.
*
* AUTHOR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. AUTHOR SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*
* THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
* CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
* PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
* NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
* SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
* SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
* PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). AUTHOR
* SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
* HIGH RISK ACTIVITIES.
*/
public class CondVar {
private BusyFlag SyncVar;
public CondVar() {
this(new BusyFlag());
}
public CondVar(BusyFlag sv) {
SyncVar = sv;
}
public void cvWait() throws InterruptedException {
cvTimedWait(SyncVar, 0);
}
public void cvWait(BusyFlag sv) throws InterruptedException {
cvTimedWait(sv, 0);
}
public void cvTimedWait(int millis) throws InterruptedException {
cvTimedWait(SyncVar, millis);
}
public void cvTimedWait(BusyFlag sv, int millis) throws InterruptedException {
int i = 0;
InterruptedException errex = null;
synchronized (this) {
// You must own the lock in order to use this method
if (sv.getBusyFlagOwner() != Thread.currentThread()) {
throw new IllegalMonitorStateException("current thread not owner");
}
// Release the lock (Completely)
while (sv.getBusyFlagOwner() == Thread.currentThread()) {
i++;
sv.freeBusyFlag();
}
// Use wait() method
try {
if (millis == 0) {
wait();
} else {
wait(millis);
}
} catch (InterruptedException iex) {
errex = iex;
}
}
// Obtain the lock (Return to original state)
for (; i>0; i--) {
sv.getBusyFlag();
}
if (errex != null) throw errex;
return;
}
public void cvSignal() {
cvSignal(SyncVar);
}
public synchronized void cvSignal(BusyFlag sv) {
// You must own the lock in order to use this method
if (sv.getBusyFlagOwner() != Thread.currentThread()) {
throw new IllegalMonitorStateException("current thread not owner");
}
notify();
}
public void cvBroadcast() {
cvBroadcast(SyncVar);
}
public synchronized void cvBroadcast(BusyFlag sv) {
// You must own the lock in order to use this method
if (sv.getBusyFlagOwner() != Thread.currentThread()) {
throw new IllegalMonitorStateException("current thread not owner");
}
notifyAll();
}
}
Early return
public class EarlyReturn extends Object {
private volatile int value;
public EarlyReturn(int v) {
value = v;
}
public synchronized void setValue(int v) {
if (value != v) {
value = v;
notifyAll();
}
}
public synchronized boolean waitUntilAtLeast(int minValue, long msTimeout)
throws InterruptedException {
System.out.println("value=" + value + ",minValue=" + minValue);
long endTime = System.currentTimeMillis() + msTimeout;
long msRemaining = msTimeout;
while ((value < minValue) && (msRemaining > 0L)) {
System.out.println("about to: wait(" + msRemaining + ")");
wait(msRemaining);
msRemaining = endTime - System.currentTimeMillis();
System.out.println("back from wait(), new msRemaining="
+ msRemaining);
}
System.out.println("leaving waitUntilAtLeast() - " + "value=" + value
+ ",minValue=" + minValue);
// May have timed out, or may have met value,
return (value >= minValue);
}
public static void main(String[] args) {
try {
final EarlyReturn er = new EarlyReturn(0);
Runnable r = new Runnable() {
public void run() {
try {
Thread.sleep(1500);
er.setValue(2);
Thread.sleep(500);
er.setValue(3);
Thread.sleep(500);
er.setValue(4);
} catch (Exception x) {
x.printStackTrace();
}
}
};
Thread t = new Thread(r);
t.start();
System.out.println("waitUntilAtLeast(5, 3000)");
long startTime = System.currentTimeMillis();
boolean retVal = er.waitUntilAtLeast(5, 3000);
long elapsedTime = System.currentTimeMillis() - startTime;
System.out.println(elapsedTime + " ms, retVal=" + retVal);
} catch (InterruptedException ix) {
ix.printStackTrace();
}
}
}
Exception call back
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class ExceptionCallbackMain extends Object implements ExceptionListener {
private int exceptionCount;
public ExceptionCallbackMain() {
exceptionCount = 0;
}
public void exceptionOccurred(Exception x, Object source) {
exceptionCount++;
System.err.println("EXCEPTION #" + exceptionCount + ", source="
+ source);
x.printStackTrace();
}
public static void main(String[] args) {
ExceptionListener xListener = new ExceptionCallbackMain();
ExceptionCallback ec = new ExceptionCallback(xListener);
}
}
class ExceptionCallback extends Object {
private Set exceptionListeners;
private Thread internalThread;
private volatile boolean noStopRequested;
public ExceptionCallback(ExceptionListener[] initialGroup) {
init(initialGroup);
}
public ExceptionCallback(ExceptionListener initialListener) {
ExceptionListener[] group = new ExceptionListener[1];
group[0] = initialListener;
init(group);
}
public ExceptionCallback() {
init(null);
}
private void init(ExceptionListener[] initialGroup) {
System.out.println("initializing...");
exceptionListeners = Collections.synchronizedSet(new HashSet());
if (initialGroup != null) {
for (int i = 0; i < initialGroup.length; i++) {
addExceptionListener(initialGroup[i]);
}
}
noStopRequested = true;
Runnable r = new Runnable() {
public void run() {
try {
runWork();
} catch (Exception x) {
sendException(x);
}
}
};
internalThread = new Thread(r);
internalThread.start();
}
private void runWork() {
try {
makeConnection();
} catch (IOException x) {
sendException(x);
}
String str = null;
int len = determineLength(str);
}
private void makeConnection() throws IOException {
String portStr = "jexp20";
int port = 0;
try {
port = Integer.parseInt(portStr);
} catch (NumberFormatException x) {
sendException(x);
port = 80;
}
connectToPort(port);
}
private void connectToPort(int portNum) throws IOException {
throw new IOException("connection refused");
}
private int determineLength(String s) {
return s.length();
}
public void stopRequest() {
noStopRequested = false;
internalThread.interrupt();
}
public boolean isAlive() {
return internalThread.isAlive();
}
private void sendException(Exception x) {
if (exceptionListeners.size() == 0) {
x.printStackTrace();
return;
}
synchronized (exceptionListeners) {
Iterator iter = exceptionListeners.iterator();
while (iter.hasNext()) {
ExceptionListener l = (ExceptionListener) iter.next();
l.exceptionOccurred(x, this);
}
}
}
public void addExceptionListener(ExceptionListener l) {
if (l != null) {
exceptionListeners.add(l);
}
}
public void removeExceptionListener(ExceptionListener l) {
exceptionListeners.remove(l);
}
public String toString() {
return getClass().getName() + "[isAlive()=" + isAlive() + "]";
}
}
interface ExceptionListener {
public void exceptionOccurred(Exception x, Object source);
}
Finds a resource with the given name.
/*
* Copyright 2003-2008 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import java.io.InputStream;
/**
*
* @author
* @version $Id: ClassUtils.java 685685 2008-08-13 21:43:27Z nbubna $
* @since 1.5
*/
public class Main {
/**
* Finds a resource with the given name. Checks the Thread Context
* classloader, then uses the System classloader. Should replace all
* calls to <code>Class.getResourceAsString</code> when the resource
* might come from a different classloader. (e.g. a webapp).
* @param claz Class to use when getting the System classloader (used if no Thread
* Context classloader available or fails to get resource).
* @param name name of the resource
* @return InputStream for the resource.
*/
public static InputStream getResourceAsStream(Class claz, String name)
{
InputStream result = null;
/**
* remove leading slash so path will work with classes in a JAR file
*/
while (name.startsWith("/"))
{
name = name.substring(1);
}
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
if (classLoader == null)
{
classLoader = claz.getClassLoader();
result = classLoader.getResourceAsStream( name );
}
else
{
result= classLoader.getResourceAsStream( name );
/**
* for compatibility with texen / ant tasks, fall back to
* old method when resource is not found.
*/
if (result == null)
{
classLoader = claz.getClassLoader();
if (classLoader != null)
result = classLoader.getResourceAsStream( name );
}
}
return result;
}
}
Listing all threads and threadgroups in the VM.
public class ThreadLister {
private static void printThreadInfo(Thread t, String indent) {
if (t == null)
return;
System.out.println(indent + "Thread: " + t.getName() + " Priority: "
+ t.getPriority() + (t.isDaemon() ? " Daemon" : "")
+ (t.isAlive() ? "" : " Not Alive"));
}
/** Display info about a thread group */
private static void printGroupInfo(ThreadGroup g, String indent) {
if (g == null)
return;
int numThreads = g.activeCount();
int numGroups = g.activeGroupCount();
Thread[] threads = new Thread[numThreads];
ThreadGroup[] groups = new ThreadGroup[numGroups];
g.enumerate(threads, false);
g.enumerate(groups, false);
System.out.println(indent + "Thread Group: " + g.getName()
+ " Max Priority: " + g.getMaxPriority()
+ (g.isDaemon() ? " Daemon" : ""));
for (int i = 0; i < numThreads; i++)
printThreadInfo(threads[i], indent + " ");
for (int i = 0; i < numGroups; i++)
printGroupInfo(groups[i], indent + " ");
}
/** Find the root thread group and list it recursively */
public static void listAllThreads() {
ThreadGroup currentThreadGroup;
ThreadGroup rootThreadGroup;
ThreadGroup parent;
// Get the current thread group
currentThreadGroup = Thread.currentThread().getThreadGroup();
// Now go find the root thread group
rootThreadGroup = currentThreadGroup;
parent = rootThreadGroup.getParent();
while (parent != null) {
rootThreadGroup = parent;
parent = parent.getParent();
}
printGroupInfo(rootThreadGroup, "");
}
public static void main(String[] args) {
ThreadLister.listAllThreads();
}
}
Return a new instance of the given class. Checks the ThreadContext classloader first, then uses the System classloader.
/*
* Copyright 2003-2008 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import java.io.InputStream;
/**
*
* @author
* @version $Id: ClassUtils.java 685685 2008-08-13 21:43:27Z nbubna $
* @since 1.5
*/
public class Main {
/**
* Return a new instance of the given class. Checks the ThreadContext
* classloader first, then uses the System classloader. Should replace all
* calls to <code>Class.forName( claz ).newInstance()</code> (which only
* calls the System class loader) when the class might be in a different
* classloader (e.g. in a webapp).
*
* @param clazz the name of the class to instantiate
* @return an instance of the specified class
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static Object getNewInstance(String clazz)
throws ClassNotFoundException,IllegalAccessException,InstantiationException
{
return getClass(clazz).newInstance();
}
/**
* Return the specified class. Checks the ThreadContext classloader first,
* then uses the System classloader. Should replace all calls to
* <code>Class.forName( claz )</code> (which only calls the System class
* loader) when the class might be in a different classloader (e.g. in a
* webapp).
*
* @param clazz the name of the class to instantiate
* @return the requested Class object
* @throws ClassNotFoundException
*/
public static Class getClass(String clazz) throws ClassNotFoundException
{
/**
* Use the Thread context classloader if possible
*/
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader != null)
{
try
{
return Class.forName(clazz, true, loader);
}
catch (ClassNotFoundException E)
{
/**
* If not found with ThreadContext loader, fall thru to
* try System classloader below (works around bug in ant).
*/
}
}
/**
* Thread context classloader isn"t working out, so use system loader.
*/
return Class.forName(clazz);
}
}
Sleep utilities
public final class TimerUtil
{
public final static void wait(int waitTime)
{
try {
Thread.sleep(waitTime);
}
catch (Exception e) {}
}
public final static void waitRandom(int time)
{
int waitTime = (int)(Math.random() * (double)time);
try {
Thread.sleep(waitTime);
}
catch (Exception e) {}
}
}
Thread-based logging utility
/*
* Copyright Aduna (http://www.aduna-software.ru/) (c) 1997-2006.
*
* Licensed under the Aduna BSD-style license.
*/
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
/**
* Thread-based logging utility. The ThreadLog requires threads to register
* themselves with the ThreadLog. In this registration procedure, a mapping is
* made between the thread calling the registration method and an instance of
* ThreadLog. This mapping is later used in all calls to logging-methods to
* look-up whether these messages should be logged, and where they should be
* logged.
* <p>
* Log messages are assigned a "level of importance". From high to low, these
* levels are ERROR, WARNING, STATUS and TRACE. Messages can be suppressed based
* on these levels. If a Thread registers itself with log level WARNING, only
* errors and warning will be logged; status messages and traces will be
* suppressed.
*/
public class ThreadLog {
/*-----------*
* Constants *
*-----------*/
public static final int NONE = 0;
public static final int ERROR = 1;
public static final int WARNING = 2;
public static final int STATUS = 3;
public static final int TRACE = 4;
public static final int ALL = 5;
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
/*------------------*
* Static variables *
*------------------*/
static SimpleDateFormat _formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
static String[] _levelNames = { "NONE ", "ERROR ", "WARNING", "STATUS ", "TRACE ", "ALL " };
/** Keeps track of which Threads maps to which ThreadLogs. */
static InheritableThreadLocal<ThreadLog> _threadContext = new InheritableThreadLocal<ThreadLog>();
static ThreadLog _defaultThreadLog;
/** Maps (absolute) file paths to PrintWriters. */
static HashMap<String, Writer> _writerTable = new HashMap<String, Writer>();
/**
* set the ThreadLog to log warning and error messages to System.err by
* default
*/
static {
setDefaultLog(null, WARNING);
}
/*----------------*
* Static methods *
*----------------*/
/**
* Sets a default log file for all threads that have not registered
* themselves. Logging calls from any of these threads to ThreadLog will be
* logged to the specified log file if their priority is equal to or higher
* then the specified log level. If "logFile" is equal to "null", messages
* will be printed to System.err.
*
* @param logFile
* The file to log to, or <tt>null</tt> to log messages to
* System.err.
* @param logLevel
* One of the constants ERROR, WARNING, STATUS, TRACE, or ALL.
* @see #ERROR
* @see #WARNING
* @see #STATUS
* @see #TRACE
* @see #ALL
*/
public static void setDefaultLog(String logFile, int logLevel) {
if (_defaultThreadLog == null) {
_defaultThreadLog = new ThreadLog();
}
Writer logWriter = null;
try {
logWriter = _getLogWriter(logFile);
}
catch (IOException e) {
System.err.println(e.getMessage());
e.printStackTrace();
try {
logWriter = _getLogWriter(null);
}
catch (IOException ignore) {
// ignore
}
}
_defaultThreadLog.setLogWriter(logWriter);
_defaultThreadLog.setLogLev(logLevel);
}
/**
* Unsets the default log file for all threads that have not registered
* themselves.
*/
public static void unsetDefaultLog() {
_defaultThreadLog = null;
}
public static int getDefaultLogLevel() {
if (_defaultThreadLog == null) {
_defaultThreadLog = new ThreadLog();
}
return _defaultThreadLog.getLogLev();
}
/**
* Registers the calling thread with the ThreadLog. Logging calls to
* ThreadLog will be logged to the specified log file if their priority is
* equal to or higher then the specified log level. If "logFile" is equal to
* "null", messages will be printed to System.err.
*
* @param logFile
* The file to log to, or <tt>null</tt> to log messages to
* System.err.
* @param logLevel
* One of the constants ERROR, WARNING, STATUS, TRACE, or ALL.
* @see #ERROR
* @see #WARNING
* @see #STATUS
* @see #TRACE
* @see #ALL
*/
public static void registerThread(String logFile, int logLevel) {
ThreadLog threadLog = _createThreadLog();
Writer logWriter = null;
try {
logWriter = _getLogWriter(logFile);
}
catch (IOException e) {
System.err.println(e.getMessage());
e.printStackTrace();
try {
logWriter = _getLogWriter(null);
}
catch (IOException ignore) {
// ignore
}
}
threadLog.setLogWriter(logWriter);
threadLog.setLogLev(logLevel);
Thread currentThread = Thread.currentThread();
threadLog.setThreadName(currentThread.getName());
}
/**
* Changes the log level for the calling thread.
*
* @param logLevel
* One of the constants ERROR, WARNING, STATUS, TRACE, or ALL.
* @see #ERROR
* @see #WARNING
* @see #STATUS
* @see #TRACE
*/
public static void setLogLevel(int logLevel) {
ThreadLog threadLog = null;
synchronized (_threadContext) {
threadLog = _threadContext.get();
}
if (threadLog != null) {
threadLog.setLogLev(logLevel);
}
}
public static int getLogLevel() {
ThreadLog threadLog = _getThreadLog();
if (threadLog != null) {
return threadLog._logLevel;
}
else {
return NONE;
}
}
public static boolean logLevelEnabled(int logLevel) {
return getLogLevel() >= logLevel;
}
/**
* Creates a ThreadLog for the thread calling this method. If the thread has
* already created a ThreadLog before, this existing ThreadLog will be
* returned.
*/
static ThreadLog _createThreadLog() {
ThreadLog threadLog = null;
synchronized (_threadContext) {
threadLog = _threadContext.get();
if (threadLog == null) {
threadLog = new ThreadLog();
_threadContext.set(threadLog);
}
}
return threadLog;
}
/**
* Gets a (possibly shared) Writer to the specified logFile.
*/
static Writer _getLogWriter(String logFile)
throws IOException
{
Writer logWriter = null;
String absPath = null;
File file = null;
if (logFile != null) {
file = new File(logFile);
absPath = file.getAbsolutePath();
}
synchronized (_writerTable) {
logWriter = _writerTable.get(absPath);
if (logWriter == null) {
// Create a new log writer
if (absPath != null) {
// Check if parent directory exists yet
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
logWriter = new FileWriter(absPath, true);
}
else {
logWriter = new OutputStreamWriter(System.err);
}
_writerTable.put(absPath, logWriter);
}
}
return logWriter;
}
/**
* Deregisters the calling thread with the ThreadLog. Logging calls to
* ThreadLog on the calling thread will no longer be logged.
*/
public static void deregisterThread() {
synchronized (_threadContext) {
_threadContext.set(null);
}
}
/**
* Gets the ThreadLog for the thread calling this method. If no ThreadLog is
* available, null will be returned.
*/
static ThreadLog _getThreadLog() {
ThreadLog threadLog = null;
synchronized (_threadContext) {
threadLog = _threadContext.get();
}
if (threadLog == null) {
threadLog = _defaultThreadLog;
}
return threadLog;
}
/*-----------*
* Variables *
*-----------*/
/** Writer for the log file. */
Writer _logWriter;
/** Log level */
int _logLevel;
/**
* Name of the thread.
*/
String _threadName;
/*--------------*
* Constructors *
*--------------*/
ThreadLog() {
this(null, ALL);
}
ThreadLog(Writer logWriter) {
this(logWriter, ALL);
}
ThreadLog(Writer logWriter, int logLevel) {
setLogWriter(logWriter);
setLogLev(logLevel);
_threadName = null;
}
/*---------*
* Methods *
*---------*/
void setLogWriter(Writer logWriter) {
_logWriter = logWriter;
}
void setLogLev(int logLevel) {
_logLevel = logLevel;
}
int getLogLev() {
return _logLevel;
}
void setThreadName(String threadName) {
_threadName = threadName;
}
String getThreadName() {
return _threadName;
}
void doLog(String msg, Object arg, int level) {
// First check log level
if (_logLevel < level) {
return;
}
// Create log message
String logMsg = _createLogMessage(msg, arg, level, _threadName);
// Write log message
// Synchronize on _logWriter to prevent mixed lines
try {
synchronized (_logWriter) {
_logWriter.write(logMsg);
_logWriter.flush();
}
}
catch (Exception e) {
e.printStackTrace();
}
}
static String _createLogMessage(String msg, Object arg, int level, String threadName) {
StringBuilder logMsg = new StringBuilder(128);
logMsg.append(_formatter.format(new Date()));
if (threadName != null) {
logMsg.append(" [");
logMsg.append(threadName);
logMsg.append("]");
}
logMsg.append(" [");
logMsg.append(_levelNames[level]);
logMsg.append("] ");
logMsg.append(msg);
if (arg != null) {
logMsg.append(": ");
if (arg instanceof Throwable) {
Throwable throwable = (Throwable)arg;
logMsg.append(throwable.getMessage());
logMsg.append(LINE_SEPARATOR);
java.io.StringWriter stackTrace = new java.io.StringWriter();
java.io.PrintWriter pw = new java.io.PrintWriter(stackTrace);
throwable.printStackTrace(pw);
logMsg.append(stackTrace.toString());
}
else {
String argString = arg.toString();
if (argString.contains("\n") || argString.contains("\r")) {
// argument formatted with newlines, do not append on same line
logMsg.append(LINE_SEPARATOR);
}
logMsg.append(argString);
}
}
logMsg.append(LINE_SEPARATOR);
return logMsg.toString();
}
/*------------------------*
* Static utility methods *
*------------------------*/
/**
* Logs an error.
*
* @param msg
* A indicative message for the error.
*/
public static void error(String msg) {
_log(msg, null, ERROR);
}
/**
* Logs an error.
*
* @param msg
* A indicative message for the error.
* @param arg
* An argument related to the error. In case <tt>arg</tt> is an
* instance of <tt>java.lang.Throwable</tt>, the message and stack
* trace of the argument will be logged.
*/
public static void error(String msg, Object arg) {
_log(msg, arg, ERROR);
}
/**
* Logs a warning.
*
* @param msg
* A indicative message for the warning.
*/
public static void warning(String msg) {
_log(msg, null, WARNING);
}
/**
* Logs a warning.
*
* @param msg
* A indicative message for the warning.
* @param arg
* An argument related to the warning. In case <tt>arg</tt> is an
* instance of <tt>java.lang.Throwable</tt>, the message and stack
* trace of the argument will be logged.
*/
public static void warning(String msg, Object arg) {
_log(msg, arg, WARNING);
}
/**
* Logs a message.
*
* @param msg
* A indicative message for the message.
*/
public static void log(String msg) {
_log(msg, null, STATUS);
}
/**
* Logs a message.
*
* @param msg
* A indicative message for the message.
* @param arg
* An argument related to the message. In case <tt>arg</tt> is an
* instance of <tt>java.lang.Throwable</tt>, the message and stack
* trace of the argument will be logged.
*/
public static void log(String msg, Object arg) {
_log(msg, arg, STATUS);
}
/**
* Logs a trace message.
*
* @param msg
* A indicative message for the trace message.
*/
public static void trace(String msg) {
_log(msg, null, TRACE);
}
/**
* Logs a trace message.
*
* @param msg
* A indicative message for the trace message.
* @param arg
* An argument related to the trace message. In case <tt>arg</tt> is
* an instance of <tt>java.lang.Throwable</tt>, the message and
* stack trace of the argument will be logged.
*/
public static void trace(String msg, Object arg) {
_log(msg, arg, TRACE);
}
/**
* Logs a message on the specified level.
*
* @param msg
* A indicative message for the trace message.
* @param arg
* An argument related to the trace message. In case <tt>arg</tt> is
* an instance of <tt>java.lang.Throwable</tt>, the message and
* stack trace of the argument will be logged.
* @param level
* One of the constants ERROR, WARNING, STATUS, TRACE, or ALL.
* @see #ERROR
* @see #WARNING
* @see #STATUS
* @see #TRACE
* @see #ALL
*/
protected static void _log(String msg, Object arg, int level) {
ThreadLog threadLog = _getThreadLog();
if (threadLog != null) {
threadLog.doLog(msg, arg, level);
}
}
}
Transition Detector
public class TransitionDetectorMain extends Object {
private static Thread startTrueWaiter(final TransitionDetector td,
String name) {
Runnable r = new Runnable() {
public void run() {
try {
while (true) {
print("about to wait for false-to-"
+ "true transition, td=" + td);
td.waitForFalseToTrueTransition();
print("just noticed for false-to-"
+ "true transition, td=" + td);
}
} catch (InterruptedException ix) {
return;
}
}
};
Thread t = new Thread(r, name);
t.start();
return t;
}
private static Thread startFalseWaiter(final TransitionDetector td,
String name) {
Runnable r = new Runnable() {
public void run() {
try {
while (true) {
print("about to wait for true-to-"
+ "false transition, td=" + td);
td.waitForTrueToFalseTransition();
print("just noticed for true-to-"
+ "false transition, td=" + td);
}
} catch (InterruptedException ix) {
return;
}
}
};
Thread t = new Thread(r, name);
t.start();
return t;
}
private static void print(String msg) {
String name = Thread.currentThread().getName();
System.err.println(name + ": " + msg);
}
public static void main(String[] args) {
try {
TransitionDetector td = new TransitionDetector(false);
Thread threadA = startTrueWaiter(td, "threadA");
Thread threadB = startFalseWaiter(td, "threadB");
Thread.sleep(200);
print("td=" + td + ", about to set to "false"");
td.setValue(false);
Thread.sleep(200);
print("td=" + td + ", about to set to "true"");
td.setValue(true);
Thread.sleep(200);
print("td=" + td + ", about to pulse value");
td.pulseValue();
Thread.sleep(200);
threadA.interrupt();
threadB.interrupt();
} catch (InterruptedException x) {
x.printStackTrace();
}
}
}
class TransitionDetector extends Object {
private boolean value;
private Object valueLock;
private Object falseToTrueLock;
private Object trueToFalseLock;
public TransitionDetector(boolean initialValue) {
value = initialValue;
valueLock = new Object();
falseToTrueLock = new Object();
trueToFalseLock = new Object();
}
public void setValue(boolean newValue) {
synchronized (valueLock) {
if (newValue != value) {
value = newValue;
if (value) {
notifyFalseToTrueWaiters();
} else {
notifyTrueToFalseWaiters();
}
}
}
}
public void pulseValue() {
// Sync on valueLock to be sure that no other threads
// get into setValue() between these two setValue()
// calls.
synchronized (valueLock) {
setValue(!value);
setValue(!value);
}
}
public boolean isTrue() {
synchronized (valueLock) {
return value;
}
}
public void waitForFalseToTrueTransition() throws InterruptedException {
synchronized (falseToTrueLock) {
falseToTrueLock.wait();
}
}
private void notifyFalseToTrueWaiters() {
synchronized (falseToTrueLock) {
falseToTrueLock.notifyAll();
}
}
public void waitForTrueToFalseTransition() throws InterruptedException {
synchronized (trueToFalseLock) {
trueToFalseLock.wait();
}
}
private void notifyTrueToFalseWaiters() {
synchronized (trueToFalseLock) {
trueToFalseLock.notifyAll();
}
}
public String toString() {
return String.valueOf(isTrue());
}
}
View current Threads in a table
import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
public class ThreadViewer extends JPanel {
private ThreadViewerTableModel tableModel = new ThreadViewerTableModel();
public ThreadViewer() {
JTable table = new JTable(tableModel);
table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
TableColumnModel colModel = table.getColumnModel();
int numColumns = colModel.getColumnCount();
for (int i = 0; i < numColumns - 1; i++) {
TableColumn col = colModel.getColumn(i);
col.sizeWidthToFit();
col.setPreferredWidth(col.getWidth() + 5);
col.setMaxWidth(col.getWidth() + 5);
}
JScrollPane sp = new JScrollPane(table);
setLayout(new BorderLayout());
add(sp, BorderLayout.CENTER);
}
public void dispose() {
tableModel.stopRequest();
}
protected void finalize() throws Throwable {
dispose();
}
public static void main(String[] args) {
JFrame f = new JFrame();
ThreadViewer viewer = new ThreadViewer();
f.setContentPane(viewer);
f.setSize(500, 300);
f.setVisible(true);
f.setDefaultCloseOperation(1);
// Keep the main thread from exiting by blocking
// on wait() for a notification that never comes.
Object lock = new Object();
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException x) {
}
}
}
}
class ThreadViewerTableModel extends AbstractTableModel {
private Object dataLock;
private int rowCount;
private Object[][] cellData;
private Object[][] pendingCellData;
private final int columnCount;
private final String[] columnName;
private final Class[] columnClass;
private Thread internalThread;
private volatile boolean noStopRequested;
public ThreadViewerTableModel() {
rowCount = 0;
cellData = new Object[0][0];
String[] names = { "Priority", "Alive", "Daemon", "Interrupted",
"ThreadGroup", "Thread Name" };
columnName = names;
Class[] classes = { Integer.class, Boolean.class, Boolean.class,
Boolean.class, String.class, String.class };
columnClass = classes;
columnCount = columnName.length;
dataLock = new Object();
noStopRequested = true;
Runnable r = new Runnable() {
public void run() {
try {
runWork();
} catch (Exception x) {
// in case ANY exception slips through
x.printStackTrace();
}
}
};
internalThread = new Thread(r, "ThreadViewer");
internalThread.setPriority(Thread.MAX_PRIORITY - 2);
internalThread.setDaemon(true);
internalThread.start();
}
private void runWork() {
Runnable transferPending = new Runnable() {
public void run() {
transferPendingCellData();
fireTableDataChanged();
}
};
while (noStopRequested) {
try {
createPendingCellData();
SwingUtilities.invokeAndWait(transferPending);
Thread.sleep(5000);
} catch (InvocationTargetException tx) {
tx.printStackTrace();
stopRequest();
} catch (InterruptedException x) {
Thread.currentThread().interrupt();
}
}
}
public void stopRequest() {
noStopRequested = false;
internalThread.interrupt();
}
public boolean isAlive() {
return internalThread.isAlive();
}
private void createPendingCellData() {
Thread[] thread = findAllThreads();
Object[][] cell = new Object[thread.length][columnCount];
for (int i = 0; i < thread.length; i++) {
Thread t = thread[i];
Object[] rowCell = cell[i];
rowCell[0] = new Integer(t.getPriority());
rowCell[1] = new Boolean(t.isAlive());
rowCell[2] = new Boolean(t.isDaemon());
rowCell[3] = new Boolean(t.isInterrupted());
rowCell[4] = t.getThreadGroup().getName();
rowCell[5] = t.getName();
}
synchronized (dataLock) {
pendingCellData = cell;
}
}
private void transferPendingCellData() {
synchronized (dataLock) {
cellData = pendingCellData;
rowCount = cellData.length;
}
}
public int getRowCount() {
return rowCount;
}
public Object getValueAt(int row, int col) {
return cellData[row][col];
}
public int getColumnCount() {
return columnCount;
}
public Class getColumnClass(int columnIdx) {
return columnClass[columnIdx];
}
public String getColumnName(int columnIdx) {
return columnName[columnIdx];
}
public static Thread[] findAllThreads() {
ThreadGroup group = Thread.currentThread().getThreadGroup();
ThreadGroup topGroup = group;
while (group != null) {
topGroup = group;
group = group.getParent();
}
int estimatedSize = topGroup.activeCount() * 2;
Thread[] slackList = new Thread[estimatedSize];
int actualSize = topGroup.enumerate(slackList);
Thread[] list = new Thread[actualSize];
System.arraycopy(slackList, 0, list, 0, actualSize);
return list;
}
}