单例 变 多例--多ClassLoader实例加载

 

         最近做项目,想要把一个服务线程Worker做成单例模式。但是,发现在多ClassLoader实例加载下,很难控制Worker实例的数量,实现全局的单例。


类加载层次

         我们系统为了方便各个服务热启动,给每个服务分配一个ClassLoader实例,来实现动态加载。结合JDK的ClassLoader加载链,可以分成下面5层:

BootstrapLoader -> ExtClassLoader -> AppClassLoader -> ServerClassLoader-> ServiceClassLoader

         前三层是JDK的,这里就不细说,具体可以参考见我的另一篇博文:JVM类加载机制-ClassLoader

         后面两层,ServerClassLoader负责加载我们框架的主要代码;而ServiceClassLoader负责加载各个服务的业务代码。这里实现服务的热启动加载就是通过多个ServiceClassLoader实例来实现的。

 

单例 变 多例

         我在服务A创建了一个单例的Worker来给其他服务调用。如下图所示,我的服务A和其他服务平级,所以使用各自ServiceClassLoader实例来加载。

         我启动服务A,会生成一个worker单例,而当其他服务调用worker的时候,他们的ServiceClassLoader实例会找不到服务A的worker,又会重新创建一个worker实例。这样worker就没办法单例了,至少每个调用到的服务都会创建一个。

 

方案对比

目前方案

         只能保证在一个ServiceClassLoader实例中worker的单例,即每个服务中worker的单例,而不能实现整个应用worker的单例。

         而且,在worker代码变更,需要重新加载的时候,调用到worker的服务也需要重启。

 

单例化worker

         把worker交给ServerClassLoader来加载。因为整个应用只有一个ServerClassLoader实例,所以能保证整个应用worker的单例。

         但是这样就不能热加载worker了。如果worker的代码变化了,只能重启整个应用,丧失了服务热加载的特性。同时,弱化代码层次,worker就得归到系统层,而不再是服务层了。

 

Demo

         说了半天,下面来复现一下多个ClassLoader实例加载多个“单例”的情况。

         首先,我们简单实现一个单例:使用静态成员,实际上是通过JVM的机制来保证 MultiLoadBean.own的全局单例性。

         下面代码,如果使用JDK的ClassLoader来加载,在MultiLoadBean被加载的时候,只会创建一个MultiLoadBean.own并打印。

public class MultiLoadBean {
    private static MultiLoadBean own = new MultiLoadBean();
    static {
        System.out.println(own);
    }
}

 

         为了实现多个ClassLoader实例加载MultiLoadBean的效果,下面要做两件事情:

l  把classpath加载好的MultiLoadBean.class移到classpath外,如和src同级的urlClass目录下,并按路径存放好:/urlClass/jscai/classLoader/MultiLoadBean.class  。

l  使用不同的URLClassLoader实例来加载MultiLoadBean。

public static void main(String[] args) throws Exception {
    String folder = "urlClass";
    String url = "file:/" + System.getProperty("user.dir") + File.separator + folder + File.separator;
    URL[] arrUrl = new URL[]{new URL(url)};
    ClassLoaderloader1 = new URLClassLoader(arrUrl);
    ClassLoaderloader2 = new URLClassLoader(arrUrl);
    loader1.loadClass("jscai.classLoader.MultiLoadBean").newInstance();
    loader2.loadClass("jscai.classLoader.MultiLoadBean").newInstance();
}
=============Console==================

    jscai.classLoader.MultiLoadBean@3764951d

    jscai.classLoader.MultiLoadBean@3cd1a2f1

 

         如上所示,MultiLoadBean被打印了两次,而且内存地址都不同,说明MultiLoadBean被加载了两次。这就是“单例”被多个ClassLoader实例加载,就会变成“多例”

         要变回单例很简单,就是把MultiLoadBean.class放回classpath,再运行一次,就会发现,又回到了单例时代了。

         具体原因见我的另一篇博文:JVM类加载机制-ClassLoader

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值