Mega Code Archive

 
Categories / Java / Development Class
 

Performing deadlock detection programmatically within the application using the java lang management API

/*  * @(#)Deadlock.java  1.5 05/11/17  *   * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.  *   * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions are met:  *   * -Redistribution of source code must retain the above copyright notice, this  *  list of conditions and the following disclaimer.  *   * -Redistribution in binary form must reproduce the above copyright notice,   *  this list of conditions and the following disclaimer in the documentation  *  and/or other materials provided with the distribution.  *   * Neither the name of Sun Microsystems, Inc. or the names of contributors may   * be used to endorse or promote products derived from this software without   * specific prior written permission.  *   * This software is provided "AS IS," without a warranty of any kind. ALL   * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING  * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE  * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")  * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE  * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST   * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,   * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY   * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,   * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  *   * You acknowledge that this software is not designed, licensed or intended  * for use in the design, construction, operation or maintenance of any  * nuclear facility.  */ /*  * @(#)Deadlock.java  1.5 05/11/17  */ import static java.lang.management.ManagementFactory.THREAD_MXBEAN_NAME; import static java.lang.management.ManagementFactory.getThreadMXBean; import static java.lang.management.ManagementFactory.newPlatformMXBeanProxy; import java.io.IOException; import java.lang.management.LockInfo; import java.lang.management.MonitorInfo; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.management.InstanceNotFoundException; import javax.management.IntrospectionException; import javax.management.MBeanOperationInfo; import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; /**  * This Deadlock class demonstrates the capability of performing deadlock  * detection programmatically within the application using the  * java.lang.management API.  *   * See ThreadMonitor.java for the use of java.lang.management.ThreadMXBean API.  */ public class Deadlock {   public static void main(String[] argv) {     Deadlock dl = new Deadlock();     // Now find deadlock     ThreadMonitor monitor = new ThreadMonitor();     boolean found = false;     while (!found) {       found = monitor.findDeadlock();       try {         Thread.sleep(500);       } catch (InterruptedException e) {         System.exit(1);       }     }     System.out.println("\nPress <Enter> to exit this Deadlock program.\n");     waitForEnterPressed();   }   private CyclicBarrier barrier = new CyclicBarrier(6);   public Deadlock() {     DeadlockThread[] dThreads = new DeadlockThread[6];     Monitor a = new Monitor("a");     Monitor b = new Monitor("b");     Monitor c = new Monitor("c");     dThreads[0] = new DeadlockThread("MThread-1", a, b);     dThreads[1] = new DeadlockThread("MThread-2", b, c);     dThreads[2] = new DeadlockThread("MThread-3", c, a);     Lock d = new ReentrantLock();     Lock e = new ReentrantLock();     Lock f = new ReentrantLock();     dThreads[3] = new DeadlockThread("SThread-4", d, e);     dThreads[4] = new DeadlockThread("SThread-5", e, f);     dThreads[5] = new DeadlockThread("SThread-6", f, d);     // make them daemon threads so that the test will exit     for (int i = 0; i < 6; i++) {       dThreads[i].setDaemon(true);       dThreads[i].start();     }   }   class DeadlockThread extends Thread {     private Lock lock1 = null;     private Lock lock2 = null;     private Monitor mon1 = null;     private Monitor mon2 = null;     private boolean useSync;     DeadlockThread(String name, Lock lock1, Lock lock2) {       super(name);       this.lock1 = lock1;       this.lock2 = lock2;       this.useSync = true;     }     DeadlockThread(String name, Monitor mon1, Monitor mon2) {       super(name);       this.mon1 = mon1;       this.mon2 = mon2;       this.useSync = false;     }     public void run() {       if (useSync) {         syncLock();       } else {         monitorLock();       }     }     private void syncLock() {       lock1.lock();       try {         try {           barrier.await();         } catch (InterruptedException e) {           e.printStackTrace();           System.exit(1);         } catch (BrokenBarrierException e) {           e.printStackTrace();           System.exit(1);         }         goSyncDeadlock();       } finally {         lock1.unlock();       }     }     private void goSyncDeadlock() {       try {         barrier.await();       } catch (InterruptedException e) {         e.printStackTrace();         System.exit(1);       } catch (BrokenBarrierException e) {         e.printStackTrace();         System.exit(1);       }       lock2.lock();       throw new RuntimeException("should not reach here.");     }     private void monitorLock() {       synchronized (mon1) {         try {           barrier.await();         } catch (InterruptedException e) {           e.printStackTrace();           System.exit(1);         } catch (BrokenBarrierException e) {           e.printStackTrace();           System.exit(1);         }         goMonitorDeadlock();       }     }     private void goMonitorDeadlock() {       try {         barrier.await();       } catch (InterruptedException e) {         e.printStackTrace();         System.exit(1);       } catch (BrokenBarrierException e) {         e.printStackTrace();         System.exit(1);       }       synchronized (mon2) {         throw new RuntimeException(getName() + " should not reach here.");       }     }   }   class Monitor {     String name;     Monitor(String name) {       this.name = name;     }   }   private static void waitForEnterPressed() {     try {       boolean done = false;       while (!done) {         char ch = (char) System.in.read();         if (ch < 0 || ch == '\n') {           done = true;         }       }     } catch (IOException e) {       e.printStackTrace();       System.exit(0);     }   } } /*  * @(#)ThreadMonitor.java 1.6 05/12/22  *   * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.  *   * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions are met:  *   * -Redistribution of source code must retain the above copyright notice, this  * list of conditions and the following disclaimer.  *   * -Redistribution in binary form must reproduce the above copyright notice,  * this list of conditions and the following disclaimer in the documentation  * and/or other materials provided with the distribution.  *   * Neither the name of Sun Microsystems, Inc. or the names of contributors may  * be used to endorse or promote products derived from this software without  * specific prior written permission.  *   * This software is provided "AS IS," without a warranty of any kind. ALL  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY  * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR  * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS  * LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A  * RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  * IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT  * OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,  * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS  * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  *   * You acknowledge that this software is not designed, licensed or intended for  * use in the design, construction, operation or maintenance of any nuclear  * facility.  */ /*  * @(#)ThreadMonitor.java 1.6 05/12/22  */ /**  * Example of using the java.lang.management API to dump stack trace and to  * perform deadlock detection.  *   * @author Mandy Chung  * @version %% 12/22/05  */ class ThreadMonitor {   private MBeanServerConnection server;   private ThreadMXBean tmbean;   private ObjectName objname;   // default - JDK 6+ VM   private String findDeadlocksMethodName = "findDeadlockedThreads";   private boolean canDumpLocks = true;   /**    * Constructs a ThreadMonitor object to get thread information in a remote    * JVM.    */   public ThreadMonitor(MBeanServerConnection server) throws IOException {     this.server = server;     this.tmbean = newPlatformMXBeanProxy(server, THREAD_MXBEAN_NAME, ThreadMXBean.class);     try {       objname = new ObjectName(THREAD_MXBEAN_NAME);     } catch (MalformedObjectNameException e) {       // should not reach here       InternalError ie = new InternalError(e.getMessage());       ie.initCause(e);       throw ie;     }     parseMBeanInfo();   }   /**    * Constructs a ThreadMonitor object to get thread information in the local    * JVM.    */   public ThreadMonitor() {     this.tmbean = getThreadMXBean();   }   /**    * Prints the thread dump information to System.out.    */   public void threadDump() {     if (canDumpLocks) {       if (tmbean.isObjectMonitorUsageSupported() && tmbean.isSynchronizerUsageSupported()) {         // Print lock info if both object monitor usage         // and synchronizer usage are supported.         // This sample code can be modified to handle if         // either monitor usage or synchronizer usage is supported.         dumpThreadInfoWithLocks();       }     } else {       dumpThreadInfo();     }   }   private void dumpThreadInfo() {     System.out.println("Full Java thread dump");     long[] tids = tmbean.getAllThreadIds();     ThreadInfo[] tinfos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE);     for (ThreadInfo ti : tinfos) {       printThreadInfo(ti);     }   }   /**    * Prints the thread dump information with locks info to System.out.    */   private void dumpThreadInfoWithLocks() {     System.out.println("Full Java thread dump with locks info");     ThreadInfo[] tinfos = tmbean.dumpAllThreads(true, true);     for (ThreadInfo ti : tinfos) {       printThreadInfo(ti);       LockInfo[] syncs = ti.getLockedSynchronizers();       printLockInfo(syncs);     }     System.out.println();   }   private static String INDENT = "    ";   private void printThreadInfo(ThreadInfo ti) {     // print thread information     printThread(ti);     // print stack trace with locks     StackTraceElement[] stacktrace = ti.getStackTrace();     MonitorInfo[] monitors = ti.getLockedMonitors();     for (int i = 0; i < stacktrace.length; i++) {       StackTraceElement ste = stacktrace[i];       System.out.println(INDENT + "at " + ste.toString());       for (MonitorInfo mi : monitors) {         if (mi.getLockedStackDepth() == i) {           System.out.println(INDENT + "  - locked " + mi);         }       }     }     System.out.println();   }   private void printThread(ThreadInfo ti) {     StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\"" + " Id="         + ti.getThreadId() + " in " + ti.getThreadState());     if (ti.getLockName() != null) {       sb.append(" on lock=" + ti.getLockName());     }     if (ti.isSuspended()) {       sb.append(" (suspended)");     }     if (ti.isInNative()) {       sb.append(" (running in native)");     }     System.out.println(sb.toString());     if (ti.getLockOwnerName() != null) {       System.out.println(INDENT + " owned by " + ti.getLockOwnerName() + " Id="           + ti.getLockOwnerId());     }   }   private void printMonitorInfo(ThreadInfo ti, MonitorInfo[] monitors) {     System.out.println(INDENT + "Locked monitors: count = " + monitors.length);     for (MonitorInfo mi : monitors) {       System.out.println(INDENT + "  - " + mi + " locked at ");       System.out.println(INDENT + "      " + mi.getLockedStackDepth() + " "           + mi.getLockedStackFrame());     }   }   private void printLockInfo(LockInfo[] locks) {     System.out.println(INDENT + "Locked synchronizers: count = " + locks.length);     for (LockInfo li : locks) {       System.out.println(INDENT + "  - " + li);     }     System.out.println();   }   /**    * Checks if any threads are deadlocked. If any, print the thread dump    * information.    */   public boolean findDeadlock() {     long[] tids;     if (findDeadlocksMethodName.equals("findDeadlockedThreads")         && tmbean.isSynchronizerUsageSupported()) {       tids = tmbean.findDeadlockedThreads();       if (tids == null) {         return false;       }       System.out.println("Deadlock found :-");       ThreadInfo[] infos = tmbean.getThreadInfo(tids, true, true);       for (ThreadInfo ti : infos) {         printThreadInfo(ti);         printLockInfo(ti.getLockedSynchronizers());         System.out.println();       }     } else {       tids = tmbean.findMonitorDeadlockedThreads();       if (tids == null) {         return false;       }       ThreadInfo[] infos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE);       for (ThreadInfo ti : infos) {         // print thread information         printThreadInfo(ti);       }     }     return true;   }   private void parseMBeanInfo() throws IOException {     try {       MBeanOperationInfo[] mopis = server.getMBeanInfo(objname).getOperations();       // look for findDeadlockedThreads operations;       boolean found = false;       for (MBeanOperationInfo op : mopis) {         if (op.getName().equals(findDeadlocksMethodName)) {           found = true;           break;         }       }       if (!found) {         // if findDeadlockedThreads operation doesn't exist,         // the target VM is running on JDK 5 and details about         // synchronizers and locks cannot be dumped.         findDeadlocksMethodName = "findMonitorDeadlockedThreads";         canDumpLocks = false;       }     } catch (IntrospectionException e) {       InternalError ie = new InternalError(e.getMessage());       ie.initCause(e);       throw ie;     } catch (InstanceNotFoundException e) {       InternalError ie = new InternalError(e.getMessage());       ie.initCause(e);       throw ie;     } catch (ReflectionException e) {       InternalError ie = new InternalError(e.getMessage());       ie.initCause(e);       throw ie;     }   } }