Java statik başlatıcıları iş parçacığı güvenli midir?


136

Ben bir kayıt defterinde bazı denetleyicileri başlatmak için statik bir kod bloğu kullanıyorum. Benim sorum bu nedenle, bu statik kod bloğunun sınıf ilk yüklendiğinde yalnızca bir kez kesinlikle çağrılacağını garanti edebilir miyim? Bu kod bloğu çağrıldığında garanti edemez, Classloader ilk yüklediğinde onun tahmin ediyorum anlıyorum. Statik kod bloğundaki sınıfta senkronize edebileceğimin farkındayım, ama tahminim bu aslında ne oluyor?

Basit kod örneği;

class FooRegistry {

    static {
        //this code must only ever be called once 
        addController(new FooControllerImpl());
    }

    private static void addController(IFooController controller) { 
        // ...
    }
}

ya da bunu yapmalı mıyım;

class FooRegistry {

    static {
        synchronized(FooRegistry.class) {
            addController(new FooControllerImpl());
        }
    }

    private static void addController(IFooController controller) {
        // ...
    }
}

10
Bu tasarımı sevmiyorum, çünkü test edilemez. Bağımlılık Enjeksiyonuna bir göz atın.
dfa

Yanıtlar:


199

Evet, Java statik başlatıcıları iş parçacığı için güvenlidir (ilk seçeneğinizi kullanın).

Ancak, kodun tam olarak bir kez yürütüldüğünden emin olmak istiyorsanız, sınıfın yalnızca tek bir sınıf yükleyici tarafından yüklendiğinden emin olmanız gerekir. Statik başlatma her sınıf-yükleyici için bir kez yapılır.


2
Ancak, bir sınıf birden çok sınıf yükleyicisi tarafından yüklenebilir, bu nedenle addController yine de birden fazla çağrılabilir (çağrıyı senkronize edip etmemenizden bağımsız olarak) ...
Matthew Murdoch

4
Ah o zaman bekle, yani statik kod bloğunun aslında sınıfı yükleyen her classloader için çağrıldığını söylüyoruz. Hmm ... Sanırım bu hala iyi olmalı, ancak, bir OSGI env bu tür bir kod çalıştırmak mulitple paket sınıf yükleyiciler ile nasıl çalışacağını merak im ..
simon622

1
Evet. Sınıfı yükleyen her sınıf yükleyici için statik kod bloğu çağrılır.
Matthew Murdoch

3
@ simon622 Evet, ancak her ClassLoader'da farklı bir sınıf nesnesinde çalışır. Hala aynı tam nitelikli ada sahip olan, ancak birbirine dönüştürülemeyen farklı türleri temsil eden farklı Sınıf nesneleri.
Erwin Bolwidt

1
bu, 'nihai' anahtar kelimenin örnek sahibinde en fazla şu anlama gelir mi : en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom ?
spc16670

11

Bu tembel başlatma için kullanabileceğiniz bir numara

enum Singleton {
    INSTANCE;
}

veya Java 5.0 öncesi için

class Singleton {
   static class SingletonHolder {
      static final Singleton INSTANCE = new Singleton();
   }
   public static Singleton instance() {
      return SingletonHolder.INSTANCE;
   }
}

SingletonHolder'daki statik blok bir kez iş parçacığı güvenli bir şekilde çalışacağından başka bir kilitlemeye ihtiyacınız yoktur. SingletonHolder sınıfı yalnızca instance () öğesini çağırdığınızda yüklenir


18
Bu cevabı, statik bloğun yalnızca küresel olarak bir kez yürütüleceği gerçeğine dayandırıyorsunuz - ki bu sorulan soru.
Michael Myers

2
Bunun da çok sınıflı yükleyici ortamında güvenli olmadığını düşünüyorum.
Ahmad

2
@Ahmad Çok sınıflı yükleyici ortamları, her uygulamanın kendi tek tonlarına sahip olmasını sağlamak için tasarlanmıştır.
Peter Lawrey

4

Her zamanki durumlarda, statik başlatmadaki her şey gerçekleşir-bu sınıfı kullanan her şeyden önce, bu nedenle senkronizasyon genellikle gerekli değildir. Bununla birlikte, sınıfa, statik sezgiselleştiricinin çağırdığı her şey için erişilebilir (diğer statik başlatıcıların çağrılmasına neden olma dahil).

Bir sınıf, yüklenen ancak hemen başlatılması gerekmeyen bir sınıf tarafından yüklenebilir. Tabii ki, bir sınıf, sınıf yükleyicilerin çoklu örnekleri tarafından yüklenebilir ve böylece aynı ada sahip birden fazla sınıf haline gelebilir.


3

Evet, bir çeşit

Bir staticbaşlatıcı yalnızca bir kez çağrılır, bu nedenle bu tanımla iş parçacığı güvenlidir - staticiş parçacığına çekişme sağlamak için başlatıcıyı iki veya daha fazla çağırmanız gerekir .

Bununla birlikte, staticbaşlatıcılar birçok şekilde kafa karıştırıyor. Gerçekten adlandırıldıkları belirli bir düzen yok. staticBaşlatıcıları birbirine bağlı iki sınıfınız varsa, bu gerçekten kafa karıştırıcı olur . Ve bir sınıf kullanırsanız ancak staticbaşlatıcısının ayarlayacağı şeyi kullanmazsanız , sınıf yükleyicinin statik başlatıcıyı çağıracağı garanti edilmez.

Son olarak, senkronize ettiğiniz nesneleri unutmayın. Bunun gerçekten sorduğunuz şey olmadığını anlıyorum, ancak sorunuzun addController()iş parçacığı güvenliğini sağlamanız gerekip gerekmediğini gerçekten sormadığından emin olun .


5
Bunların çağrıldığı çok tanımlanmış bir sıra vardır: Kaynak kodundaki sıraya göre.
mafu

Ayrıca, sonuçlarını kullanırsanız kullanın, her zaman çağrılırlar. Bu Java 6'da değiştirilmedikçe
mafu

8
Bir sınıf içinde, başlatıcılar kodu izler. İki veya daha fazla sınıf göz önüne alındığında, hangi sınıfın ilk olarak başlatılacağı, bir sınıfın başka bir başlamadan% 100 önce başlatılıp başlatılmayacağı veya işlerin nasıl "araya sokulduğu" olarak tanımlanmamıştır. Örneğin, her iki sınıfın birbirlerine atıfta bulunan statik initalializatörleri varsa, işler çirkinleşir. Başlatıcıları çağıran başka bir sınıfa statik bir final int'e başvurabileceğiniz yollar olduğunu düşündüm, ancak noktayı şu ya da bu şekilde tartışmayacağım
Matt

Çirkinleşiyor ve bundan kaçınırdım. Ancak, döngülerin nasıl çözüleceğinin tanımlanmış bir yolu vardır. "Java Programlama Dili 4. Baskı" dan alıntı: Sayfa: 75, Bölüm: 2.5.3. Statik Başlatma: "Döngüler olursa, X'in statik başlatıcıları yalnızca Y yönteminin çağrıldığı noktaya kadar yürütülür. Y, X yöntemini çağırdığında, bu yöntem henüz yürütülmemiş olan statik başlatıcıların geri kalanıyla çalışır. "
JMI MADISON

0

Evet, Statik başlatıcılar yalnızca bir kez çalıştırılır. Daha fazla bilgi için bunu okuyun .


2
Hayır, birden fazla çalıştırılabilirler.
Sınırlı Kefaret

5
Hayır CLASSLOADER başına bir kez çalıştırılabilir.
ruurd

Temel cevap: Statik başlatma yalnızca bir kez çalışır. Gelişmiş yanıt: Statik başlatma her sınıf yükleyici için bir kez çalışır. İlk yorum kafa karıştırıcıdır çünkü kelime öbeği bu iki yanıtı karıştırır.
JMI MADISON

-4

Temel olarak, tek bir örnek istediğiniz için, bunu az çok eski moda bir şekilde yapmalı ve singleton nesnenizin bir kez ve sadece bir kez başlatıldığından emin olmalısınız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.