コードを動的にロードするために、2 つのカスタム クラス ローダーを作成しました。
最初のものは、Jar からコードをロードします。
package com.customweb.build.bean.include;
import java.net.URL;
import java.net.URLClassLoader;
import com.customweb.build.process.ILeafClassLoader;
public class JarClassLoader extends URLClassLoader implements ILeafClassLoader {
public JarClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
public Class<?> findClassWithoutCycles(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
if (c != null) {
return c;
}
return findClass(name);
}
@Override
protected Class<?> findClass(String qualifiedClassName) throws ClassNotFoundException {
synchronized (this.getParent()) {
synchronized (this) {
return super.findClass(qualifiedClassName);
}
}
}
@Override
public URL findResourceWithoutCycles(String name) {
return super.findResource(name);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
synchronized (this.getParent()) {
synchronized (this) {
return super.loadClass(name);
}
}
}
}
もう一方のクラス・ローダーは、複数のクラス・ローダーを使用して、他のクラス・ローダーのクラスにアクセスできるようにします。最初のインスタンスの初期化中に、このクラス ローダーのインスタンスを親として設定しました。このサイクルを断ち切るために、'findClassWithoutCycles' メソッドを使用します。
package com.customweb.build.process;
import java.net.URL;
import java.security.SecureClassLoader;
import java.util.ArrayList;
import java.util.List;
public class MultiClassLoader extends SecureClassLoader {
private final List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
public MultiClassLoader(ClassLoader parent) {
super(parent);
}
public void addClassLoader(ClassLoader loader) {
this.classLoaders.add(loader);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
for (ClassLoader loader : classLoaders) {
try {
if (loader instanceof ILeafClassLoader) {
return ((ILeafClassLoader) loader).findClassWithoutCycles(name);
} else {
return loader.loadClass(name);
}
} catch (ClassNotFoundException e) {
// Ignore it, we try the next class loader.
}
}
throw new ClassNotFoundException(name);
}
@Override
protected URL findResource(String name) {
for (ClassLoader loader : classLoaders) {
URL url = null;
if (loader instanceof ILeafClassLoader) {
url = ((ILeafClassLoader) loader).findResourceWithoutCycles(name);
} else {
url = loader.getResource(name);
}
if (url != null) {
return url;
}
}
return null;
}
}
しかし、このクラスローダーを使用すると、ほとんどの場合デッドロックが発生します。私はここでスレッドダンプを過ぎました: http://pastebin.com/6wZKv4Y0
Java ClassLoader は一部のメソッドで $this で同期することによってスレッドをブロックするため、最初に MultiClassLoader で同期し、次に JarClassLoader で同期しようとします。これにより、ロックを取得する順序が同じ場合にデッドロックが発生するのを防ぐことができます。しかし、ネイティブ クラス ローディング ルーチンのどこかで、クラス ローダーのロックが取得されているようです。スレッド「pool-2-thread-8」がオブジェクト「0x00000007b0f7f710」でロックされているため、この結論に達しました。しかし、ログでは、このロックがいつどのスレッドによって取得されたかを確認できません。
クラスローダーで同期を行っているスレッドを見つけるにはどうすればよいですか?
編集:MultiClassLoaderのloadClassを呼び出す前に、すべてのクラスローダーで同期することで解決しました。