Java/Threads/Utilities — различия между версиями

Материал из Java эксперт
Перейти к: навигация, поиск
 
м (1 версия)
 
(нет различий)

Текущая версия на 06:58, 1 июня 2010

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;
  }
}