你所不知道的Java之ClassLoader并行加载

在JDK1.7之前的JDK1.6 ClassLoader,是这个样子的:

protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
        }
        if (c == null) {
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
}

可以看到loadClass方法是synchronized,所以如果调用了java.lang.ClassLoader的loadClass方法,则会是对象级同步调用。

PS:请问方法的修饰符,可以被子类继承么?

为了提高效率,在JDK1.7后,ClassLoader进行了优化,可以并行加载。

如果想要ClassLoader支持并行的能力,必须满足以下条件:

  1. 目前没有创建该ClassLoader的实例。
  2. 所有该ClassLoader的父类都支持并行加载。

PS:一旦声明为并行加载,则不能回退(即改回串行加载)。

总的来说,就是在该ClassLoader和该ClasssLoader的父类中,声明以下静态代码块(需要注意静态代码块的顺序,不懂得请留言)。

static {
    ClassLoader.registerAsParallelCapable();
}
private static class ParallelLoaders {
    private ParallelLoaders() {
    }

    private static final Set<Class<? extends ClassLoader>> loaderTypes =
            Collections.newSetFromMap(new WeakHashMap<>());

    static {
        synchronized (loaderTypes) {
            loaderTypes.add(ClassLoader.class);
        }
    }
    static boolean register(Class<? extends ClassLoader> c) {
        synchronized (loaderTypes) {
            if (loaderTypes.contains(c.getSuperclass())) {
                loaderTypes.add(c);
                return true;
            } else {
                return false;
            }
        }
    }
    static boolean isRegistered(Class<? extends ClassLoader> c) {
        synchronized (loaderTypes) {
            return loaderTypes.contains(c);
        }
    }
}

ClassLoader部分代码截取:


private final ConcurrentHashMap<String, Object> parallelLockMap;

protected static boolean registerAsParallelCapable() {
    Class<? extends ClassLoader> callerClass =
        Reflection.getCallerClass().asSubclass(ClassLoader.class);
    return ParallelLoaders.register(callerClass);
}
protected Object getClassLoadingLock(String className) {
    Object lock = this;
    if (parallelLockMap != null) {
        Object newLock = new Object();
        lock = parallelLockMap.putIfAbsent(className, newLock);
        if (lock == null) {
            lock = newLock;
        }
    }
    return lock;
}

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
            }

            if (c == null) {
                long t1 = System.nanoTime();
                c = findClass(name);
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

可以看到,如果注册了ClassLoader为并行加载,则loadClass的时候,锁的粒度是className,否则锁的粒度是ClassLoader实例本身this

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容