差異處
這裏顯示兩個版本的差異處。
下次修改 | 前次修改 | ||
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> | ||
===== ===== | ===== ===== | ||
---- | ---- |