在多个自定义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; }

浙公网安备 33010602011771号