差異處
這裏顯示兩個版本的差異處。
java:basic:classloader:duplicate_class_def_error [2016/12/15 13:51] tony |
java:basic:classloader:duplicate_class_def_error [2023/06/25 09:48] |
||
---|---|---|---|
行 1: | 行 1: | ||
- | {{tag>java}} | ||
- | ====== Classloader Error: attempted duplicate class definition for name ====== | ||
- | ===== Problem ===== | ||
- | 在寫plugin相關程式時,會去override classloader做成自己想要的載入形式。而這陣子,偶爾會發生如以下Error: | ||
- | <code> | ||
- | ava.lang.LinkageError: loader (instance of org/tonylin/FunnyClassLoader): attempted duplicate class definition for name: "org/apache/commons/cli/BasicParser" | ||
- | </code> | ||
- | 而我們的實做是去extend java.net.URLClassLoader,其中關鍵程式碼如下: | ||
- | <code java> | ||
- | private Class<?> loadClass(String className) { | ||
- | Class<?> clazz = findLoadedClass(className); | ||
- | if (clazz != null) { | ||
- | return clazz; | ||
- | } | ||
- | |||
- | // load locally | ||
- | try { | ||
- | clazz = findClass(className); | ||
- | return clazz; | ||
- | } catch (ClassNotFoundException e) { | ||
- | // ignore | ||
- | } | ||
- | |||
- | // use the standard URLClassLoader (which follows normal parent | ||
- | // delegation) | ||
- | try { | ||
- | return super.loadClass(className); | ||
- | } catch (ClassNotFoundException e) { | ||
- | return null; | ||
- | } | ||
- | } | ||
- | </code> | ||
- | ===== 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> | ||
- | ===== ===== | ||
- | ---- | ||
- | \\ | ||
- | ~~DISQUS~~ | ||
- | |||