类加载器中的Java死锁

类加载器中的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

我已经编写了两个自定义类加载器来动态加载代码

第一个从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);
            }
        }
    }

}
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发行版中有一个有死锁风险的自定义类加载器,则可以通过以下规则避免死锁:

  • 确保自定义类装入器对于并发类装入是多线程安全的

    a。决定内部锁定方案。例如,java.lang.ClassLoader根据请求的类名使用锁定方案

    b。仅删除类加载器对象锁上的所有同步

    c。确保关键部分对于加载不同类的多个线程是安全的

  • 在自定义类加载器的静态初始值设定项中,调用java.lang.ClassLoader的静态方法registerasParallelable()。此注册表明自定义类装入器的所有实例都是多线程安全的

  • 检查此自定义类加载器扩展的所有类加载器类是否也在其类初始值设定项中调用registerasParallelable()方法。确保它们对于并发类加载是多线程安全的

  • 如果自定义类加载器只重写findClass(String),则不需要