类加载器中的Java死锁
我已经编写了两个自定义类加载器来动态加载代码 第一个从Jar加载代码:类加载器中的Java死锁,java,classloader,deadlock,Java,Classloader,Deadlock,我已经编写了两个自定义类加载器来动态加载代码 第一个从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 I
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);
}
}
}
}
package com.customweb.build.bean.include;
导入java.net.URL;
导入java.net.URLClassLoader;
导入com.customweb.build.process.ILeafClassLoader;
公共类JarClassLoader扩展URLClassLoader实现ILeafClassLoader{
公共类加载器(URL[]URL,类加载器父级){
超级(URL,父级);
}
@凌驾
公共类findClassWithoutCycles(字符串名称)引发ClassNotFoundException{
c类=findLoadedClass(名称);
如果(c!=null){
返回c;
}
返回findClass(名称);
}
@凌驾
受保护类findClass(字符串qualifiedClassName)引发ClassNotFoundException{
已同步(this.getParent()){
已同步(此){
返回super.findClass(qualifiedClassName);
}
}
}
@凌驾
公共URL findResourceWithoutCycles(字符串名称){
返回super.findResource(名称);
}
@凌驾
公共类loadClass(字符串名称)引发ClassNotFoundException{
已同步(this.getParent()){
已同步(此){
返回super.loadClass(名称);
}
}
}
}
另一个类加载器使用多个类加载器来允许访问其他类加载器的类。在第一个类加载器的初始化过程中,我将这个类加载器的一个实例设置为父类。为了打破循环,我使用“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;
}
}
package com.customweb.build.process;
导入java.net.URL;
导入java.security.SecureClassLoader;
导入java.util.ArrayList;
导入java.util.List;
公共类MultiClassLoader扩展了SecureClassLoader{
私有最终列表类加载器=新的ArrayList();
公共多类加载器(类加载器父级){
超级(家长);
}
公共void addClassLoader(类加载器){
this.classLoaders.add(loader);
}
@凌驾
受保护类findClass(字符串名称)引发ClassNotFoundException{
用于(类加载器:类加载器){
试一试{
if(ILeafClassLoader的加载程序实例){
return((ILeafClassLoader)loader.findClassWithoutCycles(名称);
}否则{
返回loader.loadClass(名称);
}
}catch(classnotfounde异常){
//忽略它,我们尝试下一个类装入器。
}
}
抛出新的ClassNotFoundException(名称);
}
@凌驾
受保护的URL findResource(字符串名称){
用于(类加载器:类加载器){
URL=null;
if(ILeafClassLoader的加载程序实例){
url=((ILeafClassLoader)loader.findResourceWithoutCycles(名称);
}否则{
url=loader.getResource(名称);
}
如果(url!=null){
返回url;
}
}
返回null;
}
}
但是,当我使用这个类装入器时,大多数情况下会出现死锁。我已通过此处的线程转储:
由于Java类加载器在某些方法中通过在$this上同步来阻塞线程,因此我尝试先在MultiClassLoader上同步,然后在JarClassLoader上同步。当获取锁的顺序相同时,这应该可以防止任何死锁。但似乎在本机类加载例程的某个地方获得了类加载程序上的锁。
我得出这个结论是因为线程“pool-2-thread-8”被锁定在对象“0x00000007b0f7f710”上。但是在日志中,我看不到这个锁是何时获得的,由哪个线程获得的
我怎样才能知道哪个线程在类加载器上进行同步
编辑:
我通过在调用MultiClassLoader的loadClass之前在所有类加载器上进行同步来解决这个问题。JVM在调用loadClass之前在类加载器上获得一个锁。如果通过一个JarClassLoader加载的类引用另一个类,并且JVM尝试解析该引用,则会发生这种情况。它将直接转到创建类的类加载器,锁定它并调用loadClass。但是,在再次锁定JarClassLoader之前,您正在尝试锁定父加载程序。因此,两个锁的顺序不起作用 但我看不出这两个锁中有任何一个的原因,因为您不访问任何需要同步的资源。URLClassLoader的继承内部状态由其实现本身维护 但是,如果您想向需要同步的类添加更多状态,那么应该使用不同的机制来锁定ClassLoader实例 说 如果在Java SE 7发行版中有一个有死锁风险的自定义类加载器,则可以通过以下规则避免死锁: