Ho scritto due programmi di caricamento classe personalizzati per caricare il codice in modo dinamico.Deadlock Java in Class Loader
Il primo fa caricare il codice da un vaso:
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);
}
}
}
}
L'altra classe di carico prende più caricatori di classe per consentire di accedere alle classi degli altri caricatori di classe. Durante l'inizializzazione del primo, ho impostato un'istanza di questo caricatore di classi come genitore. Per interrompere il ciclo, utilizzo il metodo '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;
}
}
Ma quando uso questi caricatori di classe ottengo il più delle volte un deadlock. Ho passato qui la discarica filo: http://pastebin.com/6wZKv4Y0
Dal momento che i blocchi Java ClassLoader in alcuni metodi il filo sincronizzando su $ questo, cerco di sincronizzazione sul MultiClassLoader prima e poi sul JarClassLoader. Questo dovrebbe prevenire eventuali deadlock, quando l'ordine di acquisizione di un lock è lo stesso. Ma sembra che da qualche parte nella routine di caricamento della classe nativa venga acquisito un blocco sul class loader. Sono giunto a questa conclusione perché il thread 'pool-2-thread-8' è bloccato sull'oggetto '0x00000007b0f7f710'. Ma nel log non riesco a vedere quando viene acquisito questo blocco e da quale thread.
Come posso sapere quale thread esegue la sincronizzazione sul classloader?
Modifica: Ho risolto il problema sincronizzandomi su tutti i programmi di caricamento classe prima di richiamare il loadClass di MultiClassLoader.
Perché la sincronizzazione sul caricatore della classe padre? Non vedo alcuna ragione per questo. – Holger
Durante la definizione della classe, ClassLoader viene utilizzato come Blocco (sincronizza (questo)). Quando viene effettuata una chiamata loadClass() in MultiClassLoader, anche questo ClassLoader viene sincronizzato. Mezzi: ci sono casi in cui il genitore (MultiClassLoader) viene sincronizzato prima che JarClassLoader sia sincronizzato. In questo caso si entra in una situazione di stallo. Sincronizzando puoi evitare questo deadlock. –
Non vedo come aggiungere più sincronizzazione dovrebbe essere in grado di evitare un deadlock. La tua domanda dimostra che questo concetto non funziona. Comunque spero che la mia risposta possa essere d'aiuto. – Holger