我寫了兩個自定義類加載器來動態加載代碼。類加載器中的Java死鎖
第一個不負載代碼從一個Jar:
package com.customweb.build.bean.include;
import java.net.URL;
import java.net.URLClassLoader;
import com.customweb.build.process.ILeafClassLoader;
public class JarClassLoader extends URLClassLoader implements ILeafClassLoader {
public JarClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
public Class<?> findClassWithoutCycles(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
if (c != null) {
return c;
}
return findClass(name);
}
@Override
protected Class<?> findClass(String qualifiedClassName) throws ClassNotFoundException {
synchronized (this.getParent()) {
synchronized (this) {
return super.findClass(qualifiedClassName);
}
}
}
@Override
public URL findResourceWithoutCycles(String name) {
return super.findResource(name);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
synchronized (this.getParent()) {
synchronized (this) {
return super.loadClass(name);
}
}
}
}
其他類加載器需要多個類加載器,以允許訪問其他類加載器的類。在第一個初始化期間,我將這個類加載器的一個實例設置爲父類。要打破這個循環,我使用方法'findClassWithoutCycles'。
package com.customweb.build.process;
import java.net.URL;
import java.security.SecureClassLoader;
import java.util.ArrayList;
import java.util.List;
public class MultiClassLoader extends SecureClassLoader {
private final List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
public MultiClassLoader(ClassLoader parent) {
super(parent);
}
public void addClassLoader(ClassLoader loader) {
this.classLoaders.add(loader);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
for (ClassLoader loader : classLoaders) {
try {
if (loader instanceof ILeafClassLoader) {
return ((ILeafClassLoader) loader).findClassWithoutCycles(name);
} else {
return loader.loadClass(name);
}
} catch (ClassNotFoundException e) {
// Ignore it, we try the next class loader.
}
}
throw new ClassNotFoundException(name);
}
@Override
protected URL findResource(String name) {
for (ClassLoader loader : classLoaders) {
URL url = null;
if (loader instanceof ILeafClassLoader) {
url = ((ILeafClassLoader) loader).findResourceWithoutCycles(name);
} else {
url = loader.getResource(name);
}
if (url != null) {
return url;
}
}
return null;
}
}
但是,當我使用這個類裝載機時,我大部分時間都處於死鎖狀態。我過去在這裏的線程轉儲: http://pastebin.com/6wZKv4Y0
由於一些方法的線程通過$此同步,我嘗試先在的JarClassLoader上MultiClassLoader同步的Java類加載器塊。這應該防止任何死鎖,當獲取鎖的順序相同時。但是,似乎在本地類加載例程中的某處會獲取類加載器的鎖。 我得出這個結論是因爲線程'pool-2-thread-8'被鎖定在對象'0x00000007b0f7f710'上。但在日誌中,我無法看到何時獲取此鎖以及通過哪個線程。
如何找出哪個線程在類加載器上進行同步?
編輯: 我通過在調用MultiClassLoader的loadClass之前同步所有類加載器來解決它。
爲什麼要在父類加載器上同步?我沒有看到任何理由。 – Holger
在類的定義過程中,ClassLoader被用作Lock(同步(this))。當一個調用在MultiClassLoader中完成loadClass()時,這個ClassLoader也被同步。意思是:有些情況下,在JarClassLoader同步之前,父(MultiClassLoader)是同步的。在這種情況下,你會輸入一個死鎖。通過同步你可以避免這種僵局。 –
我看不出如何添加更多同步應該能夠避免死鎖。你的問題證明這個概念是行不通的。不過,我希望我的回答會有所幫助。 – Holger