import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class Deadlock { public static void main( String args[] ) throws ClassNotFoundException, InstantiationException, IllegalAccessException { // Setup File dir = new File( "classes" ); if ( !dir.exists() ) { dir.mkdir(); } for( String name: new String[] { "Deadlock$DummyClass.class", "Deadlock$DummyChild.class", "Deadlock$Runner.class", "Deadlock$WorkerThread.class" } ) { File file = new File( name ); if ( file.exists() ) { file.renameTo( new File( dir, name ) ); } } ContextClassloader.setup( Deadlock.class.getClassLoader() ); LoopbackClassloader.get(); ClassLoader loader = LoopbackClassloader.get(); Class runner = (Class)Class.forName( "Deadlock$Runner", true, loader ); new Thread( runner.newInstance() ).start(); } public static class DummyChildClass { } public static class DummyClass { private final static DummyChildClass dummy; static { System.out.println( "Static initializer in thread " + Thread.currentThread().getName() + " w/ classloader " + DummyClass.class.getClassLoader() + " and context classloader " + Thread.currentThread().getContextClassLoader() ); dummy = new DummyChildClass(); } public static DummyChildClass getDummy() { return dummy; } } public static class WorkerThread extends Thread { private Runner parent; public WorkerThread( Runner parent ) { this.parent = parent; } public void run() { Thread.currentThread().setPriority( Thread.MIN_PRIORITY ); ContextClassloader.setup( this.getClass().getClassLoader() ); try { synchronized( this.parent ) { this.parent.wait(); } new DummyClass(); } catch( Exception e ) { e.printStackTrace(); } } } public static class Runner implements Runnable { private long value; public void run() { for( int i = 0; i < 300; i++ ) { WorkerThread thread = new WorkerThread( this ); thread.setName( "Locker-" + i ); thread.start(); } System.out.println( "Waiting for all threads to start..." ); try { Thread.sleep( 3000 ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized( this ) { this.notifyAll(); } } public long getValue() { return this.value; } } public static class ContextClassloader extends ClassLoader { private static ContextClassloader loader; private static final Object lock = new Object(); public static void setup( ClassLoader other ) { synchronized( lock ) { if ( loader == null ) { loader = new ContextClassloader( LoopbackClassloader.get() ); } } Thread.currentThread().setContextClassLoader( loader ); } public ContextClassloader(ClassLoader other) { super( other ); } @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { System.out.println( "ContextClassLoader.loadClass(" + name + ")" ); ClassLoader previous = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader( ClassLoader.getSystemClassLoader() ); Class rtn = ((LoopbackClassloader)this.getParent()).findClass(name); Thread.currentThread().setContextClassLoader( previous ); if ( resolve ) { resolveClass( rtn ); } System.out.println( "ContextClassLoader.loadClass(" + name + ") returning" ); return rtn; } } public static class LoopbackClassloader extends ClassLoader { private static LoopbackClassloader instance; private synchronized static void init() { instance = new LoopbackClassloader(); } public static ClassLoader get() { if ( instance == null ) { init(); } return instance; } @Override public Class findClass(String name) throws ClassNotFoundException { System.out.println( "LoopbackClassloader.findClass(" + name + ") in thread " + Thread.currentThread().getName() + " instance " + this ); Class cls = super.findLoadedClass(name); try { if ( cls == null ) { System.out.println( "LoopbackClassloader calling Class.forName(" + name + ") in thread " + Thread.currentThread().getName() + " instance " + this ); cls = Class.forName( name, true, Thread.currentThread().getContextClassLoader() ); System.out.println( "LoopbackClassloader returning from Class.forName(" + name + ") in thread " + Thread.currentThread().getName() + " instance " + this ); return cls; } else { return cls; } } catch( ClassNotFoundException e ) { // Ignore this, load from JAR file } File input = new File( "classes", name.replace( ".", "/" ) + ".class" ); InputStream fis = null; try { fis = new FileInputStream( input ); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int data = fis.read(); while(data != -1){ buffer.write(data); data = fis.read(); } byte[] classData = buffer.toByteArray(); System.out.println( "LoopbackClassloader class with name " + name + " in thread " + Thread.currentThread().getName() ); cls = defineClass( name, classData, 0, classData.length ); Thread.yield(); } catch( Exception e ) { throw new ClassNotFoundException( "IOException reading file", e ); } finally { if ( fis != null ) { try { fis.close(); } catch (IOException e) { } } } return cls; } } }