6

Eş zamanlılık geri dönüş. Şimdiye kadar double checked locking değişkeninin çalışması için değişkenin volatile olarak bildirilmesi gerektiği açıktır. Ama sonra çift kilitli kilitleme aşağıdaki gibi kullanılır.Çift denetlemeli kilitleme ile düzenli HashMap

class Test<A, B> { 

    private final Map<A, B> map = new HashMap<>(); 

    public B fetch(A key, Function<A, B> loader) { 
     B value = map.get(key); 
     if (value == null) { 
      synchronized (this) { 
       value = map.get(key); 
       if (value == null) { 
        value = loader.apply(key); 
        map.put(key, value); 
       } 
      } 
     } 
     return value; 
    } 

} 

Neden gerçekten ConcurrentHashMap olmak zorunda değil düzenli HashMap geliyor? Tüm harita modifikasyonları synchronized bloğunda yapılır ve kod tekrarlayıcıları kullanmaz, bu nedenle teknik olarak "eşzamanlı modifikasyon" problemleri olmamalıdır. Ben kavram değil API kullanımı hakkında soruyorum olarak

putIfAbsent/computeIfAbsent kullanımını öneren kaçının :) Bu API kullanarak HashMap vs ConcurrentHashMap konuya katkıda sürece.

Güncelleme 2016-12-30

Bu soru yapısını değiştirmez aşağıda Holger "HashMap.get tarafından bir açıklama ile cevap verdi, fakat get bir çağırma dışında olduğundan put sizin çağırma. Does edildi Senkronize bloğun eşzamanlı olarak gerçekleşen bir put işleminin eksik durumunu görebilir. " Teşekkürler!

+1

Bazı konduktan sonra harita yeniden boyutlandırılacağından ve kesinlikle açılmamış çağrılarınızı kesecektir. –

+5

putIfAbsent/computeIfAbsent atomiktir ve sadece kolaylık sağlamak için değil, –

+0

Thomas, lütfen "çağrıları ara" konusunda ayrıntılı bilgi verir misiniz? Belgelerdeki eşzamanlı kotalar ve yineleyiciler için yalnızca açık referanslar buldum ve "hashmap eşzamanlı erişim" için arama yaparken –

cevap

15

Bu soru, yanıtlaması zor olan çok sayıda sayı üzerinde toplanıyor.

Bu kod yalnızca tek bir iş parçacığından çağrılırsa, o zaman bunu çok karmaşık hale getiriyorsunuz demektir; senkronizasyona ihtiyacınız yok. Ama açıkça bu senin niyetin değil.

Birden çok iş parçacığı, HashMap.get() öğesine herhangi bir eşitleme yapmadan yetki veren getirme yöntemini çağırır. HashMap iş parçacığı güvenli değil. Bam, hikayenin sonu. Çift kontrol edilen kilitlemeyi simüle etmeye çalışıyorsanız bile fark etmez; Gerçek şu ki, bir harita üzerinde get() ve put() numaralarını çağırmak, HashMap'un dahili değişebilir veri yapılarını, tüm kod yollarında tutarlı bir eşitleme olmadan işleyecektir ve bunları birden çok iş parçacığından eşzamanlı olarak çağırabileceğiniz için zaten ölüsünüz.

(Ayrıca, muhtemelen HashMap.get()'un salt bir okuma işlemi olduğunu düşünebilirsiniz, fakat bu yanlıştır. HashMap aslında bir LinkedHashMap ise (HashMap'ın bir alt sınıfıdır) LinkedHashMap.get() erişim sırasını günceller. iç veri yapılarına yazmayı içerir - burada, eşzamanlı olarak senkronizasyon olmadan. Ama eğer() yazı yazmıyorsa bile, kodunuz hala bozuktur.)

Başparmak: zeki olduğunu düşündüğünüzde Senkronizasyondan kaçınmanıza izin veren hile, neredeyse kesinlikle yanlışsınız.

+0

ikinci kontrol değeri == null derleyici tarafından atılır mı? –

+0

Cevabınız için teşekkür ederiz. Bu sadece sorunu açıklamakla kalmaz, aynı zamanda 'get()' metodunun uygulama detayları hakkında değerli bilgiler verir. – AlexR

+0

Brian, cevabın ilk yarısına katılıyorum. Sözleşme, HashMap'in senkronize edilmemesi ve bu şekilde kullanılması gerektiğidir. İkinci yarısı bogus çünkü soru somut sınıfları listeler. Ve uygulamadan bahsettiğinize göre, tedarik edilen Java kaynağına göre HashMap.get mevcut Java7/8 uygulamasında yapıyı değiştirmez. Ve yine, ilk yarıya katılıyorum ve HashMap.get –