玩轉Java虛擬機(五)

打卡學習JVM,第五天

本人學習過程中所整理的代碼,源碼地址

- 線程上下文類加載器

public class MyTest24 {
    public static void main(String[] args) {
      	System.out.println(Thread.currentThread().getContextClassLoader());
        System.out.println(Thread.class.getClassLoader());
    }
}
  • 當前類加載器(Current Classloader):每個類都會使用自己的類加載器(即加載自身的類加載器)來去加載其他類(指的是所依賴的類),如果Class X引用了Class Y,那麼Class X的類加載器就回去加載Class Y(前提是Class Y尚未被加載)
  • 線程上下文類加載器(Context Classloader):加載器是從JDK1.2開始引入的,Thread類中的getContextClassLoader()與setContextClassLoader() 分別用來獲取和設置上下文類加載器。如果沒有用過setContextClassLoader(ClassLoader c1)進行設置的話。線程將繼承其父線程的上下文類加載器Java應用運行時的初始線程的上下文類加載器是系統類加載器。在線程中運行的代碼可以通過類加載器來加載類與資源。
public class MyTest25 implements Runnable {
   private Thread thread;
   public MyTest25() {
       this.thread = new Thread(this);
       thread.start();
   }
   @Override
   public void run() {
       ClassLoader classLoader = this.thread.getContextClassLoader();
       this.thread.setContextClassLoader(classLoader);
       System.out.println("Class:" + classLoader);
       System.out.println("Parent:"+classLoader.getParent().getClass());
   }
   public static void main(String[] args) {
       new MyTest25();
   }
}
/*Class:sun.misc.Launcher$AppClassLoader@73d16e93
Parent:class sun.misc.Launcher$ExtClassLoader*/

從運行結果可以看出,線程上下文類加載器就是系統類加載器,這是怎麼實現的呢?
在Launcher類的源碼中可以看出,是通過Thread.currentThread().setContextClassLoader(this.loader)造成的,而this.loader就是系統類加載器。

- 線程上下文類加載器的重要性

  • SPI(Service Provider Interface)
  • 父ClassLoader可以使用當前線程Thread.currentThread().getContextClassLoader()所指的定的classloader加載的類。這就改變了父ClassLoader不能使用子ClassLoader或是其他沒有直接父子關係的ClassLoader加載的類的情況,即改變了雙親委託模型。
  • 線程上下文類加載器就是當前線程的Current classloader。
  • 在雙親委託模型下,類加載是由下至上的,即下層的類加載器會委託上層 進行加載。但是對於SPI來說,有些接口是Java核心庫所提供的。而Java核心庫是由啓動類加載器來加載的,而這些接口的實現卻來自於不同的jar包(廠商提供),Java的啓動類加載器是不會加載其他 來源的jar包,這樣傳統的雙親委託模型就無法滿足SPI的要求,而通過當前線程設置上下文類加載器就可以由設置的上下文類加載器來實 現對於接口實現類的加載。

- 線程上下文類加載器的一般使用模式

  • 獲取
  • 使用
  • 還原
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try{
	Thread.currentThread().setContextClassLoader(targetTccl);
	myMethod();
}finally{
	Thread.currentThread().setContextClassLoader(classLoader);
}
  • myMethod裏面則調用了Thread.currentThread().setContextClassLoader(),獲取當前線程的上下文類加載器做某些事情
  • 如果一個類由類加載器A加載,那麼這個類的依賴類也是由相同的類加載器加載的(如果該依賴類之前沒有被加載過的話)
  • 當高層提供了統一的接口讓低層區實現,同時又要在高層加載(或實例化)低層的類時,就必須要通過線程上下文類加載器來幫助高層的ClassLoader找到並加載該類

ContextClassLoader的作用就是爲了破壞Java的類加載委託機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章