2013-09-05 39 views
5

Kodu dinamik olarak yüklemek için iki özel sınıf yükleyici yazdım.Sınıfı Yükleyicilerde Java Kilitlenme Kodu

birincisi kavanoz yük kodu yapar:

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); 
      } 
     } 
    } 

} 

diğer sınıfı yükleyici, diğer sınıf yükleyiciler sınıfları erişimine izin vermek için çok sayıda sınıf yükleyiciler alır. İlkinin başlatılması sırasında, bu sınıf yükleyicinin bir örneğini üst öğe olarak ayarladım. Döngüyü kırmak için 'findClassWithoutCycles' yöntemini kullanıyorum.

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; 
    } 
} 

Ancak bu sınıf yükleyicileri kullandığımda çoğu zaman bir kilitlenme oluyor. Burada iplik dökümü geçmiş vardır: $ bu konuda senkronize ederek iplik, ilk ve ardından JarClassLoader üzerinde MultiClassLoader üzerinde senkronize deneyin bazı yöntemlerde Java ClassLoader blokları yana http://pastebin.com/6wZKv4Y0

. Bu, bir kilit elde etme sırasının aynı olduğu durumlarda herhangi bir kilitlenmeyi önlemelidir. Ancak, yerel sınıf yükleme rutininde, sınıf yükleyicinin üzerinde bir kilit elde edildiği görülüyor. Bu sonuca vardım çünkü 'havuz-2-thread-8' parçacığı '0x00000007b0f7f710' nesnesinde kilitlendi. Fakat günlüğünde bu kilidin ne zaman elde edildiğini ve hangi iş parçacığı ile göremiyorum.

Sınıf yükleyicide hangi iş parçacığının eşitleme yaptığını nasıl öğrenebilirim?

Düzenleme: Tüm sınıf yükleyicileri, MultiClassLoader öğesinin loadClass'ını çağırmadan önce senkronize ederek çözdüm.

+0

Neden üst sınıf yükleyicide eşitleme yapıyorsunuz? Bunun için bir neden göremiyorum. – Holger

+0

Sınıfın tanımı sırasında ClassLoader, Lock (eşitleme (bu)) olarak kullanılır. Bir çağrı MultiClassLoader'da loadClass() yapıldığında, bu ClassLoader da eşitlenir. Araçlar: JarClassLoader eşitlenmeden önce ebeveyn (MultiClassLoader) eşitlendiğinde durumlar vardır. Bu durumda bir kilitlenme içine gireceksin. Senkronize ederek bu çıkmazdan kaçabilirsiniz. –

+0

Daha fazla senkronizasyon eklemenin bir kilitlenmeden nasıl kaçınabileceğini göremiyorum. Sorunuz bu kavramın işe yaramadığını kanıtlıyor. Ancak umarım cevabım yardımcı olacaktır. – Holger

cevap

4

JVM, loadClass'ı çağırmadan önce ClassLoader üzerinde bir kilit alır. Bu, JarClassLoader öğenizin biriyle yüklenen sınıf başka bir sınıfa başvurursa ve JVM bu başvuruyu çözümlemeye çalışırsa gerçekleşir. Sınıfı oluşturan, kilitleyen ve loadClass'ı çağırmak olan ClassLoader'a doğrudan gidecektir. Ama sonra JarClassLoader'ı tekrar kilitlemeden önce ana yükleyicide bir kilit almaya çalışıyorsunuz. Yani iki kilitlerin siparişi çalışmıyor.

Ancak, senkronizasyon gerektiren kaynaklara erişemediğiniz için iki kilitden herhangi birinin nedenini göremiyorum. URLClassLoader öğesinin devralınan iç durumu, kendi uygulaması tarafından korunur. Ancak, eşitleme gerektiren sınıflarınıza daha fazla durum eklemek isterseniz, ClassLoader örneklerini kilitlemek gibi farklı mekanizmalar kullanmalısınız.

  1. emin olun:

    http://docs.oracle.com/javase/7/docs/technotes/guides/lang/cl-mt.html Java SE 7 sürümü ile kilitleyebilmenize riski ile özel bir sınıf yükleyici, varsa

    , bu kuralları uygulayarak kilitlenmeleri önlemek diyor Özel sınıf yükleyiciniz eşzamanlı sınıf yüklemesi için çok parçalı güvenlidir.

    a. Dahili kilitleme düzenine karar verin. Örneğin, java.lang.ClassLoader istenen sınıf adına dayalı bir kilitleme düzeni kullanır.

    b.Yalnızca sınıf yükleyici nesne kilidindeki tüm senkronizasyonu kaldırın.

    c. Farklı sınıflar yükleyen birden fazla iş parçacığı için kritik bölümlerin güvenli olduğundan emin olun.

  2. Özel sınıf yükleyicinizin statik başlatıcısında, java.lang.ClassLoader'ın statik yöntem registerAsParallelCapable() yöntemini çağırın. Bu kayıt, özel sınıf yükleyicinizin tüm örneklerinin çoklu güvenlikli olduğunu gösterir.

  3. Bu özel sınıf yükleyici tarafından genişletilen tüm sınıf yükleyici sınıflarının, sınıf başlatıcılarında registerAsParallelCapable() yöntemini de çağırdığını kontrol edin. Eşzamanlı sınıf yüklemesi için çok parçalı olduklarından emin olun. Özel sınıf yükleyici sadece findClass (String) geçersiz kılarsa

, başka bir değişiklik gerekmez. Bu, özel sınıf bir yükleyici oluşturmak için önerilen mekanizmadır.

+0

@Holgar: Cevabınız çok yardımcı oldu. Bir sınıf yüklendiğinde JVM'nin sınıf yükleyiciyi kilitlediğini bilmiyordum. Ancak, registerAsParallelCapable() işlevini kullanmak başarılı olmadı. LoadClass() öğesini çağırmadan önce, MultiClassLoader'daki tüm sınıf yükleyicileri kilitledim. Bu, kilitleri alma sırasını korur. –