您的位置:首页 > 其它

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

2016-03-13 00:23 169 查看
在一次面试中被问到,如何在有多个不同的类加载器,并且这些类加载器的父加载器都不同,的情况下实现一个单例。

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

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

代码如下:

public Launcher() {
Launcher.ExtClassLoader var1;
try {
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader");
}

try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader");
}

Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
if(var2 != null) {
SecurityManager var3 = null;
if(!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
;
} catch (InstantiationException var6) {
;
} catch (ClassNotFoundException var7) {
;
} catch (ClassCastException var8) {
;
}
} else {
var3 = new SecurityManager();
}

if(var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}

System.setSecurityManager(var3);
}

}


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

代码如下:

private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}

Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */

/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}

/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}

/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();

/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}

g.addUnstarted();

this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
this.name = name.toCharArray();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext = AccessController.getContext();
this.target = target;
setPriority(priority);
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;

/* Set thread ID */
tid = nextThreadID();
}


如果你的单例因为多个类加载器的问题变成了多例,可以利用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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: