在多个自定义ClassLoad下的单例研究

在一次面试中被问到,如何在有多个不同的类加载器,并且这些类加载器的父加载器都不同,的情况下实现一个单例。

后来回家之后对这个问题进行了研究,现在将研究结果记录下来。

 

打开sun.misc.Launcher类,在下面的第15行代码说明在JVM启动之后,JVM中的第一个初始化的JAVA线程的上下文类加载器,被设置成了AppClassLoad的实例。

代码如下:

 1 public Launcher() {
 2         Launcher.ExtClassLoader var1;
 3         try {
 4             var1 = Launcher.ExtClassLoader.getExtClassLoader();
 5         } catch (IOException var10) {
 6             throw new InternalError("Could not create extension class loader");
 7         }
 8 
 9         try {
10             this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
11         } catch (IOException var9) {
12             throw new InternalError("Could not create application class loader");
13         }
14 
15         Thread.currentThread().setContextClassLoader(this.loader);
16         String var2 = System.getProperty("java.security.manager");
17         if(var2 != null) {
18             SecurityManager var3 = null;
19             if(!"".equals(var2) && !"default".equals(var2)) {
20                 try {
21                     var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
22                 } catch (IllegalAccessException var5) {
23                     ;
24                 } catch (InstantiationException var6) {
25                     ;
26                 } catch (ClassNotFoundException var7) {
27                     ;
28                 } catch (ClassCastException var8) {
29                     ;
30                 }
31             } else {
32                 var3 = new SecurityManager();
33             }
34 
35             if(var3 == null) {
36                 throw new InternalError("Could not create SecurityManager: " + var2);
37             }
38 
39             System.setSecurityManager(var3);
40         }
41 
42     }

 

在Thread类的init方法中,下面的第45和47行代码,会将改Thread实例的上下文类加载器,设置为父线程的上下文类加载器。由上面可知该线程的上下文类加载器会被设置成了AppClassLoad的实例。

代码如下:

 1 private void init(ThreadGroup g, Runnable target, String name,
 2                       long stackSize) {
 3         if (name == null) {
 4             throw new NullPointerException("name cannot be null");
 5         }
 6 
 7         Thread parent = currentThread();
 8         SecurityManager security = System.getSecurityManager();
 9         if (g == null) {
10             /* Determine if it's an applet or not */
11 
12             /* If there is a security manager, ask the security manager
13                what to do. */
14             if (security != null) {
15                 g = security.getThreadGroup();
16             }
17 
18             /* If the security doesn't have a strong opinion of the matter
19                use the parent thread group. */
20             if (g == null) {
21                 g = parent.getThreadGroup();
22             }
23         }
24 
25         /* checkAccess regardless of whether or not threadgroup is
26            explicitly passed in. */
27         g.checkAccess();
28 
29         /*
30          * Do we have the required permissions?
31          */
32         if (security != null) {
33             if (isCCLOverridden(getClass())) {
34                 security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
35             }
36         }
37 
38         g.addUnstarted();
39 
40         this.group = g;
41         this.daemon = parent.isDaemon();
42         this.priority = parent.getPriority();
43         this.name = name.toCharArray();
44         if (security == null || isCCLOverridden(parent.getClass()))
45             this.contextClassLoader = parent.getContextClassLoader();
46         else
47             this.contextClassLoader = parent.contextClassLoader;
48         this.inheritedAccessControlContext = AccessController.getContext();
49         this.target = target;
50         setPriority(priority);
51         if (parent.inheritableThreadLocals != null)
52             this.inheritableThreadLocals =
53                 ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
54         /* Stash the specified stack size in case the VM cares */
55         this.stackSize = stackSize;
56 
57         /* Set thread ID */
58         tid = nextThreadID();
59     }

 

如果你的单例因为多个类加载器的问题变成了多例,可以利用JAVA线程的上下文类加载器,来统一单利类的类加载器,从而解决单例变多例的问题。

代码如下:

public class RedisUtilReal {

    private static class RedisUtilHolder {
        private static volatile RedisUtilReal redisUtilReal;

        static {
            init();
        }

        private static void init() {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            try {
                Class<?> redisUtilRealClass = classLoader.loadClass(RedisUtilReal.class.getName());
                RedisUtilHolder.redisUtilReal = (RedisUtilReal) redisUtilRealClass.newInstance();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

            if (RedisUtilHolder.redisUtilReal == null) {
                System.out.println("redis util init fail.");
            } else {
                System.out.println("redis util init success.");
            }
        }
    }

    protected RedisUtilReal() {

    }



    public static RedisUtilReal getInstance() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        if (classLoader == null) {
            classLoader = RedisUtilReal.class.getClassLoader();
        }

        try {
            classLoader.loadClass(RedisUtilReal.class.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return RedisUtilHolder.redisUtilReal;
    }

 

posted @ 2016-03-13 00:23  Zetas  阅读(367)  评论(0)    收藏  举报