Birden fazla iş parçacığından java.util.HashMap'ten değer almak güvenli mi (değişiklik yok)?


138

Bir haritanın inşa edileceği bir durum vardır ve bir kez başlatıldığında, bir daha asla değiştirilmez. Bununla birlikte, bu dosyaya birden fazla iş parçacığından erişilebilir (yalnızca get (anahtar) aracılığıyla). A'yı java.util.HashMapbu şekilde kullanmak güvenli midir?

(Şu anda, mutlu bir kullanıyorum java.util.concurrent.ConcurrentHashMapve performansı artırmak için hiçbir ölçülen ihtiyacı var, ancak basit eğer sadece merak ediyorum HashMapyeterli olacaktır. Dolayısıyla, bu soru değil "? Biri kullanmalıyım" ne de bir performans sorudur. Aksine, soru "Güvenli olur mu?")


4
Buradaki birçok yanıt, iş parçacıklarının karşılıklı olarak dışlanmasıyla ilgili doğru, ancak bellek güncellemeleriyle ilgili yanlıştır. Buna göre yukarı / aşağı oy verdim, ancak yine de olumlu oylarla birçok yanlış cevap var.
Heath Borders

@ Sınırlar, a örneği statik olarak değiştirilemeyen HashMap başlatıldıysa, eşzamanlı okuma için güvenli olmalıdır (diğer iş parçacıkları güncelleme olmadığı için güncellemeleri kaçırmamış olabilirler), değil mi?
kaqqao

Statik olarak statik olarak başlatılır ve statik bloğun dışında hiçbir zaman değiştirilmezse, tüm statik başlatmalar ClassLoader. Bu kendi başına ayrı bir soruya değer. Hala gerçek performans sorunlarına neden olduğunu doğrulamak için açıkça ve senkronize ediyorum.
Heath Borders

@HeathBorders - "bellek güncellemeleri" ile ne demek istiyorsun? JVM, görünürlük, atomisite, ilişkiden önce gerçekleşen şeyleri tanımlayan , ancak "bellek güncellemeleri" gibi terimleri kullanmayan resmi bir modeldir . Tercihen JLS'nin terminolojisini kullanarak açıklığa kavuşturmalısınız.
BeeOnRope

2
@Dave - Sanırım 8 yıl sonra hala cevap aramıyorsunuz, ancak kayıt için, neredeyse tüm cevaplardaki temel karışıklık , harita nesnesine yaptığınız eylemlere odaklanmalarıdır . Nesneyi asla değiştirmediğinizi zaten açıklamışsınızdır, bu yüzden tamamen alakasızdır. Sadece potansiyel "yakaladım" daha sonra size referans yayımlamak nasıl üzere Mapaçıklamak vermedi. Güvenli bir şekilde yapmazsanız, güvenli değildir. Eğer güvenli bir şekilde yaparsanız, öyle . Cevabımdaki detaylar.
BeeOnRope

Yanıtlar:


55

Sizin deyim güvenlidir ancak ve ancak referans HashMapalmaktadır güvenle yayınlanan . İç yayınlarıyla ilgili her şeyden ziyade HashMap, güvenli yayın , yapım iş parçacığının haritaya referansı diğer iş parçacıkları tarafından nasıl görünür hale getirdiği ile ilgilidir.

Temel olarak, burada mümkün olan tek yarış HashMap, tamamen inşa edilmeden önce ona erişebilecek herhangi bir okuma iş parçacığının yapımı arasındadır. Tartışmanın çoğu, harita nesnesinin durumuna ne olduğu ile ilgilidir, ancak asla değiştirmediğiniz için bu önemsizdir - bu yüzden tek ilginç kısım HashMapreferansın nasıl yayınlandığıdır.

Örneğin, haritayı şu şekilde yayınladığınızı düşünün:

class SomeClass {
   public static HashMap<Object, Object> MAP;

   public synchronized static setMap(HashMap<Object, Object> m) {
     MAP = m;
   }
}

... ve bir noktada setMap()bir harita ile çağrılır ve diğer iş parçacıkları SomeClass.MAPharitaya erişmek için kullanılır ve şu şekilde null olup olmadığını kontrol edin:

HashMap<Object,Object> map = SomeClass.MAP;
if (map != null) {
  .. use the map
} else {
  .. some default behavior
}

Bu güvenli değil o sanki muhtemelen görünmesine rağmen. Sorun, başka bir iş parçacığının kümesi ve sonraki okuma arasında önceden gerçekleşen bir ilişki olmamasıdır SomeObject.MAP, bu nedenle okuma iş parçacığı kısmen oluşturulmuş bir harita görmekte serbesttir. Bu hemen hemen her şeyi yapabilir ve pratikte bile okuma ipliğini sonsuz bir döngüye koymak gibi şeyler yapar .

Güvenle haritayı yayınlamak için bir tesis gerekir olur-öncesi arasındaki ilişkinin referans yazılı etmek HashMap(yani yayına ) ve referans (yani tüketim) müteakip okuyucuları. Uygun bir şekilde, bunu başarmanın kolay hatırlanabilir birkaç yolu vardır [1] :

  1. Referansı uygun şekilde kilitlenmiş bir alanda değiştirin ( JLS 17.4.5 )
  2. İlklendirme depolarını yapmak için statik başlatıcı kullanın ( JLS 12.4 )
  3. Referansı değişken bir alanla ( JLS 17.4.5 ) veya bu kuralın bir sonucu olarak AtomicX sınıfları aracılığıyla değiştirin
  4. Değeri bir son alana başlatın ( JLS 17.5 ).

Senaryonuz için en ilginç olanlar (2), (3) ve (4). Özellikle, (3) doğrudan yukarıdaki kodum için geçerlidir: beyanı dönüştürürseniz MAP:

public static volatile HashMap<Object, Object> MAP;

o zaman her şey koşerdir: null olmayan bir değer gören okuyucular mutlaka mağaza ile önce bir ilişki MAPkurarlar ve böylece harita başlatma ile ilişkili tüm mağazaları görürler.

Diğer yöntemler yönteminizin anlambilimini değiştirir, çünkü (2) (statik initalizer kullanarak) ve (4) ( final kullanarak ) MAPçalışma zamanında dinamik olarak ayarlayamayacağınızı ima eder . Eğer yoksa ihtiyaç bunu, o zaman sadece beyan MAPbir şekilde static final HashMap<>ve güvenli yayın garantilidir.

Uygulamada, kurallar "hiç değiştirilmemiş nesnelere" güvenli erişim için basittir:

Doğal olarak değiştirilemeyen (beyan edilen tüm alanlarda olduğu gibi) bir nesne yayınlıyorsanız finalve:

  • Zaten beyanı anında atanacak nesneyi oluşturabilirsiniz a : sadece kullanmak final(dahil alanını static finalstatik üyeleri için).
  • Nesneyi daha sonra, referans zaten göründükten sonra atamak istiyorsunuz: uçucu bir alan kullanın b .

Bu kadar!

Uygulamada çok verimlidir. static finalÖrneğin, bir alanın kullanılması JVM'nin programın ömrü boyunca değerin değişmediğini varsaymasına ve onu yoğun şekilde optimize etmesine izin verir. finalÜye alanının kullanımı, çoğu mimarinin alanı normal alan okumasına eşdeğer bir şekilde okumasına olanak tanır ve daha fazla optimizasyonu engellemez c .

Son olarak, kullanımının bir volatileetkisi vardır: pek çok mimaride donanım engeli gerekmez (özellikle x86, özellikle okumaların okumaları geçmesine izin vermeyenler), ancak derleme zamanında bazı optimizasyon ve yeniden sıralama gerçekleşmeyebilir - ancak bu etkisi genellikle küçüktür. Buna karşılık, aslında istediğinizden daha fazlasını elde edersiniz - sadece güvenli bir şekilde yayınlamakla HashMapkalmaz HashMap, aynı referansta istediğiniz kadar çok değiştirilmemiş s kaydedebilir ve tüm okuyucuların güvenli bir şekilde yayınlanmış bir harita göreceğinden emin olabilirsiniz. .

Daha fazla ayrıntı için Shipilev'e veya Manson ve Goetz tarafından hazırlanan bu SSS'ye bakın .


[1] Doğrudan shipilev'den alıntı .


a Kulağa karmaşık geliyor, ama demek istediğim referansı inşaat zamanında atayabilirsiniz - açıklama noktasında veya yapıcıda (üye alanları) veya statik başlatıcıda (statik alanlar).

b İsteğe bağlı olarak, synchronizedalmak / ayarlamak için bir yöntem veya bir AtomicReferenceveya bir şey kullanabilirsiniz, ancak yapabileceğiniz minimum işten bahsediyoruz.

c çok zayıf hafıza modelleriyle (Benim baktığım Bazı mimariler size , Alpha) bir önceki okuma bariyer çeşit gerektirebilir finalokuma - ama bunlar bugün çok nadirdir.


never modify HashMapanlamına state of the map objectbence parçacığı güvenli. Resmi belgenin iş parçacığı için güvenli olduğunu söylemediği takdirde, Tanrı kütüphane uygulamasını bilir.
Jiang YD

@JiangYD - bazı durumlarda orada gri bir alan var: gerçekten "ne demek" ne demek istediğimizi söylersek, dahili olarak diğer iş parçacıklarında okuma veya yazma ile yarışabilecek bazı yazma işlemleri gerçekleştiren herhangi bir eylemdir . Bu yazma işlemleri dahili uygulama ayrıntıları olabilir, bu nedenle "salt okunur" gibi görünen bir işlem bile get()bazı yazma işlemleri gerçekleştirebilir, bazı istatistiklerin güncellenmesi (veya LinkedHashMaperişim siparişinin erişim sırasının güncellenmesi durumunda) diyelim . Bu yüzden iyi yazılmış bir sınıf, aşağıdakileri açıklığa kavuşturan bazı belgeler sağlamalıdır ...
BeeOnRope

... görünüşe göre "salt okunur" işlemler iplik güvenliği anlamında gerçekten dahili olarak salt okunurdur. Örneğin C ++ standart kütüphanesinde, işaretlenen üye işlevinin constbu anlamda gerçekten salt okunur olduğu bir battaniye kuralı vardır (dahili olarak, yine de yazma gerçekleştirebilirler, ancak bunlar iş parçacığı için güvenli hale getirilmelidir). constJava'da hiçbir anahtar kelime yoktur ve herhangi bir belgelenmiş battaniye garantisinin farkında değilim, ancak genel olarak standart kütüphane sınıfları beklendiği gibi davranıyor ve istisnalar belgeleniyor ( LinkedHashMapRO gibi op'ların getaçıkça güvensiz olarak belirtildiği örneğe bakın ).
BeeOnRope

@JiangYD - son olarak, orijinal sorunuza geri dönelim, HashMapçünkü aslında bu sınıf için iş parçacığı güvenlik davranışını belgelere sahibiz : Birden fazla iş parçacığı bir eşleme haritasına aynı anda erişiyorsa ve iş parçacıklarından en az biri haritayı yapısal olarak değiştirirse, harici olarak senkronize edilmelidir. (Yapısal bir değişiklik, bir veya daha fazla eşleme ekleyen veya silen herhangi bir işlemdir; yalnızca bir örneğin zaten içerdiği bir anahtarla ilişkili değeri değiştirmek yapısal bir değişiklik değildir.)
BeeOnRope 3:30 '

Dolayısıyla, HashMapsalt okunur olmasını beklediğimiz yöntemler için, yapısal olarak değiştirmedikleri için salt okunurdur HashMap. Tabii ki, bu garanti keyfi diğer Mapuygulamalar için geçerli olmayabilir , ancak soru HashMapözellikle ilgilidir.
BeeOnRope

70

Java Bellek Modeli söz konusu olduğunda tanrı Jeremy Manson'un bu konuda üç bölümlü bir blogu var - çünkü özünde "Değişmez bir HashMap'a erişmek güvenli mi?" Sorusunu soruyorsunuz - bunun cevabı evet. Ama şu soruyu yüklemelisiniz - "Benim HashMap'ım değişmez mi?" Cevap sizi şaşırtabilir - Java'nın değişmezliği belirlemek için nispeten karmaşık bir kurallar dizisi vardır.

Konu hakkında daha fazla bilgi için Jeremy'nin blog gönderilerini okuyun:

Java'da Değişmezlik Bölüm 1: http://jeremymanson.blogspot.com/2008/04/immutability-in-java.html

Java'da Değişmezlik Bölüm 2: http://jeremymanson.blogspot.com/2008/07/immutability-in-java-part-2.html

Java'da Değişmezlik Bölüm 3: http://jeremymanson.blogspot.com/2008/07/immutability-in-java-part-3.html


3
Bu iyi bir nokta, ancak hiçbir referansın kaçmadığı statik başlatma işlemine güveniyorum, bu yüzden güvenli olmalı.
Dave L.

5
Bunun yüksek puanlı bir cevap (hatta bir cevap) olduğunu göremiyorum. Birincisi, soruyu bile cevaplamıyor ve güvenli olup olmadığına karar verecek tek temel prensipten bahsetmiyor: güvenli yayın . "Cevap", "hileli olarak" anlamına gelir ve burada okuyabileceğiniz üç (karmaşık) bağlantı vardır.
BeeOnRope

İlk cümlenin sonunda soruyu cevaplıyor. Cevap olması açısından, değişmezliğin (sorunun ilk paragrafında belirtilen) basit olmadığı ve bu konuyu daha fazla açıklayan değerli kaynakların olduğu noktasını gündeme getirmektedir. Puanlar bunun bir cevap olup olmadığını değil, cevabın başkaları için "yararlı" olup olmadığını ölçer. Kabul edilen cevap, OP'nin aradığı ve cevabınızın aldığı cevap olduğu anlamına gelir.
Jesse

@Jesse, ilk cümlenin sonunda soruyu cevaplamıyor, bir sonraki cümleye işaret ettiği gibi OP sorusuna başvurabilecek ya da etmeyebilecek olan "değişmez bir nesneye erişmek güvenli mi?" Sorusunu cevaplıyor. Esasen bu, SO için iyi bir cevap olmayan neredeyse sadece bir bağlantı "kendiniz anlayın" türü cevaptır. Upvotes gelince, ben daha çok 10.5 yaşında olmanın bir fonksiyonu ve sık aranan bir konu olduğunu düşünüyorum. Son birkaç yıl içinde çok az net upvotes aldı, bu yüzden belki insanlar geliyor :).
BeeOnRope

35

Okumalar senkronizasyon açısından güvenlidir, ancak bellek açısından güvenilir değildir. Bu, Stackoverflow da dahil olmak üzere Java geliştiricileri arasında yaygın olarak yanlış anlaşılan bir şeydir. ( Kanıt için bu cevabın derecelendirmesine dikkat edin .)

Başka iş parçacıklarınız varsa, geçerli iş parçacığından bellek yazma işlemi yoksa, HashMap'in güncelleştirilmiş bir kopyasını görmeyebilirler. Bellek yazma işlemleri, senkronize edilmiş veya geçici anahtar kelimeler kullanılarak veya bazı java eşzamanlılık yapıları kullanılarak gerçekleşir.

Ayrıntılar için Brian Goetz'in yeni Java Bellek Modeli hakkındaki makalesine bakın.


İkili sunum için üzgünüm Heath, sadece benimkini gönderdikten sonra seninkini fark ettim. :)
Alexander

2
Burada hafıza etkilerini anlayan başka insanlar olduğu için mutluyum.
Heath Borders

1
Gerçekten de, hiçbir iş parçacığı düzgün başlatılmadan önce nesneyi görecek olsa da, bu durumda bu bir endişe olduğunu düşünmüyorum.
Dave L.

1
Bu tamamen nesnenin nasıl başlatıldığına bağlıdır.
Bill Michell

1
Soru, HashMap başlatıldıktan sonra, onu daha fazla güncellemek istemediğini söylüyor. Bundan sonra, sadece salt okunur bir veri yapısı olarak kullanmak istiyor. Haritasında saklanan verilerin değişmez olması koşuluyla bunu yapmak güvenli olur diye düşünüyorum.
Binita Bharati

9

Biraz daha baktıktan sonra, bunu java doc'da buldum (benimki vurgu):

Bu uygulamanın senkronize olmadığını unutmayın. Birden çok iş parçacığı bir eşleme haritasına aynı anda erişiyorsa ve iş parçacıklarından en az biri haritayı yapısal olarak değiştiriyorsa, harici olarak senkronize edilmelidir. (Yapısal bir değişiklik, bir veya daha fazla eşleme ekleyen veya silen herhangi bir işlemdir; yalnızca bir örneğin zaten içerdiği bir anahtarla ilişkili değeri değiştirmek yapısal bir değişiklik değildir.)

Bu, ifadenin tersinin doğru olduğunu varsayarsak, güvenli olacağı anlamına geliyor.


1
Bu mükemmel bir tavsiye olsa da, diğer cevapların belirttiği gibi, değişmez, güvenli bir şekilde yayınlanmış harita örneği durumunda daha nüanslı bir cevap vardır. Ama bunu sadece ne yaptığını biliyorsan yapmalısın.
Alex Miller

1
Umarım bunlar gibi sorularla ne yaptığımızı biliriz.
Dave L.

Bu gerçekten doğru değil. Diğer yanıtların belirttiği gibi, son değişiklik ile sonraki tüm "iş parçacığı güvenli" okumaları arasında önceden bir şey olmalıdır . Normalde bu, nesneyi oluşturulduktan ve değişiklikler yapıldıktan sonra güvenli bir şekilde yayınlamanız gerektiği anlamına gelir . İlk işaretlenmiş doğru cevaba bakınız.
markspace

9

Bir not, bazı durumlarda, senkronize edilmemiş bir HashMap'ten get () yönteminin sonsuz döngüye neden olabileceğidir. Bu, eşzamanlı bir put () Eşlem'in yeniden yakalanmasına neden olursa oluşabilir.

http://lightbody.net/blog/2005/07/hashmapget_can_cause_an_infini.html


1
Aslında bu CPU (belki de daha kötü) tüketmeden JVM asmak gördüm
Peter Lawrey

2
Ben düşünüyorum bu kod sonsuz döngü elde etmek artık mümkün değildir böyle yeniden yazılmıştır. Ancak yine de senkronize olmayan bir HashMap'den başka nedenlerle alıp koymamanız gerekir.
Alex Miller

@AlexMiller diğer nedenlerin yanı sıra (güvenli yayıncılığa atıfta bulunduğunuzu varsayıyorum), dokümantasyonda açıkça izin verilmedikçe bir uygulama değişikliğinin erişim kısıtlamalarını gevşetmek için bir neden olması gerektiğini düşünmüyorum. Olduğu gibi, Java 8 için HashMap Javadoc hala bu uyarıyı içerir:Note that this implementation is not synchronized. If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally.
shmosel

8

Yine de önemli bir bükülme var. Haritaya erişmek güvenlidir, ancak genel olarak tüm iş parçacıklarının HashMap ile tam olarak aynı durumu (ve dolayısıyla değerleri) göreceği garanti edilmez. Bu, bir iş parçacığı tarafından yapılan HashMap'taki değişikliklerin (örneğin, onu dolduran) yaptığı işlemin bu CPU'nun önbelleğinde oturabileceği ve bir bellek çit işlemi yapılana kadar diğer CPU'larda çalışan iş parçacıkları tarafından görülmeyeceği çok işlemcili sistemlerde olabilir. önbellek tutarlılığı sağlanarak gerçekleştirildi. Java Dil Belirtimi bu konuda açıktır: çözüm, bir bellek çit işlemi yayan bir kilit (senkronize (...)) elde etmektir. Bu nedenle, HashMap'i doldurduktan sonra her bir iş parçacığının HERHANGİ bir kilit aldığından eminseniz, HashMap tekrar değiştirilene kadar herhangi bir iş parçacığından HashMap'e erişmek bu noktadan itibaren sorun olmaz.


Ben o iş parçacığı erişme herhangi bir kilit elde edeceğini emin değilim, ama onlar başlatılıncaya kadar nesneye bir başvuru elde olmaz eminim, bu yüzden onlar eski bir kopya olabilir sanmıyorum.
Dave L.

@Alex: HashMap referansı, aynı bellek görünürlük garantilerini oluşturmak için geçici olabilir. @Dave: O ise onun ctor işi sizin iş parçacığı görünür olmadan önce yeni OBJS başvurular görmek mümkündür.
Chris Vest

@Christian Genel durumda, kesinlikle. Bu kodda öyle olmadığını söylüyordum.
Dave L.

RANDOM kilidinin alınması, tüm iş parçacığı işlemci önbelleğinin temizlenmesini garanti etmez. JVM uygulamasına bağlıdır ve büyük olasılıkla bu şekilde yapılmaz.
Pierre

Pierre ile aynı fikirdeyim, herhangi bir kilit almanın yeterli olacağını düşünmüyorum. Değişikliklerin görünmesi için aynı kilit üzerinde senkronize etmeniz gerekir.
damluar

5

Http://www.ibm.com/developerworks/java/library/j-jtp03304/ # Başlatma güvenliğine göre HashMap'inizi son bir alan haline getirebilirsiniz ve kurucu bittikten sonra güvenli bir şekilde yayınlanır.

... Yeni bellek modeli altında, bir yapıcıdaki son alanın yazılması ile başka bir iş parçacığında o nesneye paylaşılan bir başvurunun ilk yükü arasındaki önceki bir ilişkiye benzer bir şey vardır. ...


Bu cevap düşük kalitedir, @taylor gauthier'in cevabı ile aynıdır, ancak daha az ayrıntıyla.
Snicolas

1
Hımmm ... eşek olmamak için, ama geriye doğru sahipsin. Taylor, "hayır, git bu blog yayınına bak, cevap seni şaşırtabilir", oysa bu cevap aslında bilmediğim yeni bir şey ekliyor ... Bir son alanın yazılmasından önceki bir ilişki hakkında yapıcı. Bu cevap mükemmel ve okuduğuma sevindim.
Ajax

Ha? Bu, daha yüksek puanlı yanıtları kaydırdıktan sonra bulduğum tek doğru cevap. Anahtar güvenli bir şekilde yayınlandı ve hatta bundan bahseden tek cevap bu.
BeeOnRope

3

Yani tarif ettiğiniz senaryo, bir Haritaya bir grup veri koymanız gerektiğidir, o zaman doldurmayı bitirdiğinizde değişmez olarak görürsünüz. "Güvenli" yaklaşımlardan biri (bunun gerçekten değişmez olarak ele alındığını zorunlu kıldığınız anlamına gelir), referansı Collections.unmodifiableMap(originalMap)değişmez hale getirmeye hazır olduğunuzda değiştirmektir.

Eşzamanlı olarak kullanıldığında haritaların ne kadar kötü başarısız olabileceğine ve bahsettiğim önerilen geçici çözüm için bu hata geçit girişine göz atın : bug_id = 6423457


2
Bu "değişmezliği" zorladığı için "güvenlidir", ancak iplik güvenliği konusunu ele almaz. Haritaya UnmodifiableMap sarmalayıcı ile erişmek güvenliyse, onsuz güvenlidir ve bunun tersi de geçerlidir.
Dave L.

2

Bu soru Brian Goetz'in "Uygulamada Java Eşzamanlılığı" kitabında ele alınmıştır (Liste 16.8, sayfa 350):

@ThreadSafe
public class SafeStates {
    private final Map<String, String> states;

    public SafeStates() {
        states = new HashMap<String, String>();
        states.put("alaska", "AK");
        states.put("alabama", "AL");
        ...
        states.put("wyoming", "WY");
    }

    public String getAbbreviation(String s) {
        return states.get(s);
    }
}

Yana statesolarak bildirilmiş finalve bunun başlatma sahibinin sınıf yapıcı içerisinde gerçekleştirilir sonradan bu haritayı okur herhangi bir iş parçacığı başka hiçbir konu sağlanan yapıcı bitirir, haritanın içeriğini değiştirmeye çalışacağız zamanın olarak görmeye garantilidir.


1

Tek iş parçacıklı kodda bile, bir ConcurrentHashMap öğesinin HashMap ile değiştirilmesinin güvenli olmayabileceği konusunda uyarılmalıdır. ConcurrentHashMap null değerini anahtar veya değer olarak yasaklar. HashMap onları yasaklamaz (sormayın).

Bu nedenle, mevcut kodunuzun kurulum sırasında (muhtemelen bir tür başarısızlık durumunda) koleksiyona boş değer ekleyebilmesi durumunda, koleksiyonun açıklandığı gibi değiştirilmesi işlevsel davranışı değiştirir.

Bununla birlikte, başka bir şey yapmamanız koşuluyla, bir HashMap'ten eşzamanlı okumalar güvenlidir.

[Düzenle: “eşzamanlı okumalar” ile, eşzamanlı modifikasyonların olmadığı anlamına gelir.

Diğer cevaplar bunun nasıl sağlanacağını açıklar. Bunun bir yolu haritayı değişmez yapmaktır, ancak gerekli değildir. Örneğin, JSR133 bellek modeli, bir iş parçacığının başlatılmasını senkronize bir eylem olarak açıkça tanımlar, yani A iş parçacığında B iş parçacığını başlatmadan önce yapılan değişiklikler B iş parçacığında görülebilir.

Amacım Java Bellek Modeli ile ilgili daha ayrıntılı cevaplarla çelişmemek. Bu yanıt, eşzamanlılık sorunlarının yanı sıra, ConcurrentHashMap ile HashMap arasında en az bir API farkı olduğunu ve bunun da diğeriyle değiştirilen tek iş parçacıklı bir programı bile bozabileceğini belirtmek için tasarlanmıştır.]


Uyarı için teşekkürler, ancak null anahtarları veya değerleri kullanma denemesi yok.
Dave L.

Olmayacağını düşündüm. koleksiyonlardaki null'lar Java'nın çılgın bir köşesidir.
Steve Jessop

Bu yanıta katılmıyorum. "Bir HashMap'ten eşzamanlı okumalar güvenlidir" tek başına yanlıştır. Okumaların değişebilir veya değişmez bir haritaya karşı gerçekleşip gerçekleşmediğini belirtmez. Doğru olması için "Değişmez bir HashMap'tan eşzamanlı okumalar güvenlidir" yazmalıdır
Taylor Gautier

2
Kendinize bağlantı verdiğiniz makalelere göre değil: şart, haritanın değiştirilmemesi (ve önceki değişikliklerin tüm okuyucu konuları tarafından görülebilir olması) değil, değiştirilemez olması (Java'da teknik bir terimdir ve güvenlik için yeterli fakat gerekli olmayan koşul).
Steve Jessop

Ayrıca bir not ... bir sınıfın başlatılması aynı kilit üzerinde dolaylı olarak senkronize edilir (evet, statik alan başlatıcılarda kilitlenebilir), bu nedenle başlatmanız statik olarak gerçekleşirse, başlatma tamamlanmadan önce başka birinin görmesi mümkün olmaz. edinilen aynı kilit üzerindeki ClassLoader.loadClass yönteminde engellenmeleri gerekir ... Ve aynı alanın farklı kopyalarına sahip farklı sınıf yükleyicileri merak ediyorsanız, doğru olursunuz ... ancak bu dikey yarış koşulları kavramı; bir sınıf yükleyicinin statik alanları bir bellek çitini paylaşır.
Ajax

0

http://www.docjar.com/html/api/java/util/HashMap.java.html

İşte HashMap kaynağı. Anlayacağınız gibi, kesinlikle hiçbir kilitleme / muteks kodu yoktur.

Bu, çok iş parçacıklı bir durumda bir HashMap'ten okumak iyi olsa da, birden fazla yazma olsaydı kesinlikle bir ConcurrentHashMap kullanacağım anlamına gelir.

İlginç olan şey hem .NET HashTable hem de Sözlük <K, V> senkronizasyon kodunda yerleşik olmasıdır.


2
Örneğin, geçici örnek değişkenlerinin dahili kullanımı nedeniyle, eşzamanlı olarak okumanın başınızı belaya sokabileceği sınıflar olduğunu düşünüyorum. Bu yüzden, muhtemelen kilitleme / muteks kodu için hızlı bir taramadan çok, kaynağı dikkatlice incelemelidir.
Dave L.

0

Başlatma ve her koyma senkronize edilmişse kaydedersiniz.

Sınıf yükleyici senkronizasyonla ilgileneceğinden aşağıdaki kod kaydedilir:

public static final HashMap<String, String> map = new HashMap<>();
static {
  map.put("A","A");

}

Aşağıdaki kod kaydedilir, çünkü uçucu yazı senkronizasyonu halleder.

class Foo {
  volatile HashMap<String, String> map;
  public void init() {
    final HashMap<String, String> tmp = new HashMap<>();
    tmp.put("A","A");
    // writing to volatile has to be after the modification of the map
    this.map = tmp;
  }
}

Üye değişkeni son ise bu da işe yarar çünkü final de değişkendir. Ve eğer yöntem bir kurucu ise.

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.