我编写了两个自定义类加载器来动态加载代码。
第一个确实从 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 之前同步所有类加载器来解决它。