我写了两个自定义类加载器来动态加载代码。类加载器中的Java死锁
第一个不负载代码从一个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
由于一些方法的线程通过$此同步,我尝试先在的JarClassLoader上MultiClassLoader同步的Java类加载器块。这应该防止任何死锁,当获取锁的顺序相同时。但是,似乎在本地类加载例程中的某处会获取类加载器的锁。 我得出这个结论是因为线程'pool-2-thread-8'被锁定在对象'0x00000007b0f7f710'上。但在日志中,我无法看到何时获取此锁以及通过哪个线程。
如何找出哪个线程在类加载器上进行同步?
编辑: 我通过在调用MultiClassLoader的loadClass之前同步所有类加载器来解决它。
为什么要在父类加载器上同步?我没有看到任何理由。 – Holger
在类的定义过程中,ClassLoader被用作Lock(同步(this))。当一个调用在MultiClassLoader中完成loadClass()时,这个ClassLoader也被同步。意思是:有些情况下,在JarClassLoader同步之前,父(MultiClassLoader)是同步的。在这种情况下,你会输入一个死锁。通过同步你可以避免这种僵局。 –
我看不出如何添加更多同步应该能够避免死锁。你的问题证明这个概念是行不通的。不过,我希望我的回答会有所帮助。 – Holger