內(nèi)部類單例是種很好的單例模式,
利用ClassLoader 線程安全的加載模式

為了更好的理解類的加載機(jī)制,我們來深入研究一下ClassLoader和他的loadClass()方法。

Java中的所有類,必須被裝載到j(luò)vm中才能運(yùn)行,這個(gè)裝載工作是由jvm中的類裝載器完成的,類裝載器所做的工作實(shí)質(zhì)是把類文件從硬盤讀取到內(nèi)存中,JVM在加載類的時(shí)候,都是通過ClassLoader的loadClass()方法來加載class的,loadClass使用雙親委派模式。
//class loader是一個(gè)負(fù)責(zé)加載classes的對象,ClassLoader類是一個(gè)抽象類,需要給出類的二進(jìn)制名稱,
//class loader嘗試定位或者產(chǎn)生一個(gè)class的數(shù)據(jù)
//,一個(gè)典型的策略是把二進(jìn)制名字轉(zhuǎn)換成文件名然后到文件系統(tǒng)中找到該文件。
public abstract class ClassLoader{
.....
}
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
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) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
load大致意思:
使用指定的二進(jìn)制名稱來加載類,這個(gè)方法的默認(rèn)實(shí)現(xiàn)按照以下順序查找類: 調(diào)用findLoadedClass(String)方法檢查這個(gè)類是否被加載過 使用父加載器調(diào)用loadClass(String)方法,如果父加載器為Null,類加載器裝載虛擬機(jī)內(nèi)置的加載器調(diào)用findClass(String)方法裝載類, 如果,按照以上的步驟成功的找到對應(yīng)的類,并且該方法接收的resolve參數(shù)的值為true,那么就調(diào)用resolveClass(Class)方法來處理類。 ClassLoader的子類最好覆蓋findClass(String)而不是這個(gè)方法。 除非被重寫,這個(gè)方法默認(rèn)在整個(gè)裝載過程中都是同步的(線程安全的)
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;
}
private final ConcurrentHashMap<String, Object> parallelLockMap;
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
if (ParallelLoaders.isRegistered(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<>();
package2certs = new ConcurrentHashMap<>();
domains =
Collections.synchronizedSet(new HashSet<ProtectionDomain>());
assertionLock = new Object();
} else {
// no finer-grained lock; lock on the classloader instance
parallelLockMap = null;
package2certs = new Hashtable<>();
domains = new HashSet<>();
assertionLock = this;
}
}
- 從源碼可以看出來 如果 parallelLockMap 為Null 方法就返回本身,不為Null的話那么就新建一個(gè)對象,然后在調(diào)用一個(gè)putIfAbsent(className, newLock);方法來給剛剛創(chuàng)建好的對象賦值
- parallelLockMap 從類的構(gòu)造方法賦值可以看到構(gòu)造函數(shù)根據(jù)一個(gè)屬性==ParallelLoaders==的==Registered==狀態(tài)的不同來給==parallelLockMap== 賦值。
- 這個(gè)ParallelLoaders是在哪賦值的呢?是在ClassLoader類中包含一個(gè)靜態(tài)內(nèi)部類private static class ParallelLoaders,在ClassLoader被加載的時(shí)候這個(gè)靜態(tài)內(nèi)部類就被初始化。
- 整理一下:
在ClassLoader類中有一個(gè)靜態(tài)內(nèi)部類ParallelLoaders,他會指定的類的并行能力,如果當(dāng)前的加載器被定位為具有并行能力,
那么他就給parallelLockMap定義,就是new一個(gè)ConcurrentHashMap<>(),那么這個(gè)時(shí)候,
我們知道如果當(dāng)前的加載器是具有并行能力的,那么parallelLockMap就不是Null,這個(gè)時(shí)候,我們判斷parallelLockMap是不是Null,
如果他是null,說明該加載器沒有注冊并行能力,那么我們沒有必要給他一個(gè)加鎖的對象,getClassLoadingLock方法直接返回this,就是當(dāng)前的加載器的一個(gè)實(shí)例。
如果這個(gè)parallelLockMap不是null,那就說明該加載器是有并行能力的,那么就可能有并行情況,那就需要返回一個(gè)鎖對象。
然后就是創(chuàng)建一個(gè)新的Object對象,調(diào)用parallelLockMap的putIfAbsent(className, newLock)方法,
這個(gè)方法的作用是:首先根據(jù)傳進(jìn)來的className,檢查該名字是否已經(jīng)關(guān)聯(lián)了一個(gè)value值,如果已經(jīng)關(guān)聯(lián)過value值,
那么直接把他關(guān)聯(lián)的值返回,如果沒有關(guān)聯(lián)過值的話,那就把我們傳進(jìn)來的Object對象作為value值,className作為Key值組成一個(gè)map返回。
然后無論putIfAbsent方法的返回值是什么,都把它賦值給我們剛剛生成的那個(gè)Object對象。
- 我們來簡單說明一下getClassLoadingLock(String className)的作用,就是: 為類的加載操作返回一個(gè)鎖對象。為了向后兼容,這個(gè)方法這樣實(shí)現(xiàn):如果當(dāng)前的classloader對象注冊了并行能力,方法返回一個(gè)與指定的名字className相關(guān)聯(lián)的特定對象,否則,直接返回當(dāng)前的ClassLoader對象。
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
如果父加載器不為空,那么調(diào)用父加載器的loadClass方法加載類,如果父加載器為空,那么調(diào)用虛擬機(jī)的加載器來加載類。
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
//如果還沒有找到,那么按順序調(diào)用find類來找到類。
......
c = findClass(name);
......
}
這個(gè)時(shí)候,我們已經(jīng)得到了加載之后的類,那么就根據(jù)resolve的值決定是否調(diào)用resolveClass方法。resolveClass方法的作用是:
鏈接指定的類。這個(gè)方法給Classloader用來鏈接一個(gè)類,如果這個(gè)類已經(jīng)被鏈接過了,那么這個(gè)方法只做一個(gè)簡單的返回。
否則,這個(gè)類將被按照 Java?規(guī)范中的Execution描述進(jìn)行鏈接。。。(繼承)
總結(jié)
-
java 類加載器
Bootstrp loader
Bootstrp加載器是用C++語言寫的,它是在Java虛擬機(jī)啟動后初始化的,它主要負(fù)責(zé)加載%JAVA_HOME%/jre/lib,-Xbootclasspath參數(shù)指定的路徑以及%JAVA_HOME%/jre/classes中的類。ExtClassLoader
Bootstrp loader加載ExtClassLoader,并且將ExtClassLoader的父加載器設(shè)置為Bootstrp loader.ExtClassLoader是用Java寫的,具體來說就是 sun.misc.Launcher$ExtClassLoader,ExtClassLoader主要加載%JAVA_HOME%/jre/lib/ext,此路徑下的所有classes目錄以及java.ext.dirs系統(tǒng)變量指定的路徑中類庫。AppClassLoader
Bootstrp loader加載完ExtClassLoader后,就會加載AppClassLoader,并且將AppClassLoader的父加載器指定為 ExtClassLoader。AppClassLoader也是用Java寫成的,它的實(shí)現(xiàn)類是 sun.misc.Launcher$AppClassLoader,另外我們知道ClassLoader中有個(gè)getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader主要負(fù)責(zé)加載classpath所指定的位置的類或者是jar文檔,它也是Java程序默認(rèn)的類加載器。

-
類加載器工作

類裝載器有載入類的需求時(shí),會先請示其Parent使用其搜索路徑幫忙載入,如果Parent 找不到,那么才由自己依照自己的搜索
類加載器 從下往上搜索 從上往下加載
-
類裝載時(shí)機(jī)
JVM規(guī)范嚴(yán)格定義了何時(shí)需要對類進(jìn)行初始化:
- 通過new關(guān)鍵字、反射、clone、反序列化機(jī)制實(shí)例化對象時(shí)。
- 調(diào)用類的靜態(tài)方法時(shí)。
- 使用類的靜態(tài)字段或?qū)ζ滟x值時(shí)。
- 通過反射調(diào)用類的方法時(shí)。
- 初始化該類的子類時(shí)(初始化子類前其父類必須已經(jīng)被初始化)。
- JVM啟動時(shí)被標(biāo)記為啟動類的類(簡單理解為具有main方法的類)。