差異處

這裏顯示兩個版本的差異處。

連向這個比對檢視

下次修改
前次修改
java:basic:classloader:duplicate_class_def_error [2016/12/15 13:44]
tony 建立
java:basic:classloader:duplicate_class_def_error [2023/06/25 09:48] (目前版本)
行 8: 行 8:
 而我們的實做是去extend java.net.URLClassLoader,其中關鍵程式碼如下:​ 而我們的實做是去extend java.net.URLClassLoader,其中關鍵程式碼如下:​
 <code java> <code java>
-private ​Class<?>​ loadClass(String className) {+public ​Class<?>​ loadClass(String className) ​throws ClassNotFoundException ​{
  Class<?>​ clazz = findLoadedClass(className);​  Class<?>​ clazz = findLoadedClass(className);​
  if (clazz != null) {  if (clazz != null) {
行 31: 行 31:
 } }
 </​code>​ </​code>​
 +目的是為了先讀取已載入的class,若尚未載入則優先搜尋classpath中是否有此class,最後才去找parent。
 ===== Solutions ===== ===== Solutions =====
 +我寫了一個簡單的單元測試去trace這個問題,而內容為:​
 +  * 新增一個jar檔到FunnyClassLoader的classpath中。
 +  * 產生5個thread去load某個jar檔中的class。
 +  * 驗證正確完成的工作數量為5。
 +<code java>
 +@Test
 +public void testConcurrentLoad() throws Exception{
 + mClassLoader = new FunnyClassLoader(Thread.currentThread().getContextClassLoader());​
 + mClassLoader.addURL(new File("​./​testdata/​commons-cli-1.2.jar"​));​
 +
  
 + List<​Thread>​ threads = new ArrayList<>​();​
 + List<​String>​ passThread = new ArrayList<>​();​
 + int size = 5;
 + CountDownLatch latch = new CountDownLatch(size);​
 +
 + for( int i = 0 ; i < size ; i ++ ){
 +
 + Thread t = new Thread(()->​{
 + try {
 + mClassLoader.loadClass("​org.apache.commons.cli.BasicParser"​);​
 + System.out.println("​pass:​ " + Thread.currentThread().getName());​
 + passThread.add(Thread.currentThread().getName());​
 + } catch (Exception e) {
 + e.printStackTrace();​
 + } finally {
 + latch.countDown();​
 + }
 + });
 + threads.add(t);​
 + }
 + threads.parallelStream().forEach(t->​t.start());​
 +
 + latch.await();​
 +
 + assertEquals(threads.size(),​ passThread.size());​
 +}
 +</​code>​
 +最後發現問題是由於:​ 當兩個thread同時存取loadclass時,若class已被define過,就會產生此錯誤。最後寫法是:​
 +<code java>
 +@Override
 +public Class<?>​ loadClass(String className) throws ClassNotFoundException {
 +   ​synchronized (getClassLoadingLock(className)) {
 +        // original code 
 +   }
 +}
 +</​code>​
 =====    ===== =====    =====
 ---- ----