Bir HashMap'e erişen iki birden fazla iş parçacığım varsa, ancak aynı anahtara aynı anda erişemeyeceklerini garanti edersem, bu yine de bir yarış durumuna yol açabilir mi?
Bir HashMap'e erişen iki birden fazla iş parçacığım varsa, ancak aynı anahtara aynı anda erişemeyeceklerini garanti edersem, bu yine de bir yarış durumuna yol açabilir mi?
Yanıtlar:
@ Dotsid'in cevabında şunu söylüyor:
Bir HashMap'i herhangi bir şekilde değiştirirseniz, kodunuz basitçe bozulur.
O haklıdır. Eşitleme olmadan güncellenen bir HashMap, iş parçacıkları ayrık anahtar kümeleri kullanıyor olsa bile bozulacaktır . İşte ters gidebilecek şeylerden bazıları.
Bir iş parçacığı bir iş parçacığı yaparsa put
, başka bir iş parçacığı karma haritanın boyutu için eski bir değer görebilir.
Bir iş parçacığı put
tablonun yeniden oluşturulmasını tetikleyen bir iş parçacığı yaptığında, başka bir iş parçacığı, hashtable dizi referansının, boyutunun, içeriğinin veya karma zincirlerinin geçici veya eski sürümlerini görebilir. Kaos ortaya çıkabilir.
Bir iş parçacığı put
başka bir iş parçacığı tarafından kullanılan bir anahtarla çarpışan bir anahtar için bir iş parçacığı yaptığında ve ikinci iş parçacığı put
anahtarı için bir iş parçacığı yaptığında , ikincisi karma zincir referansının eski bir kopyasını görebilir. Kaos ortaya çıkabilir.
Bir iş parçacığı, başka bir iş parçacığının anahtarlarından biriyle çarpışan bir anahtarla masayı araştırdığında, zincirde bu anahtarla karşılaşabilir. Bu tuşa eşittir çağırır ve evreler senkronize değilse, eşittir yöntemi bu anahtarda eski durumla karşılaşabilir.
Ve aynı anda yapan put
veya remove
talep eden iki iş parçacığınız varsa, yarış koşulları için çok sayıda fırsat vardır.
Üç çözüm düşünebilirim:
ConcurrentHashMap
.HashMap
ancak dışarıdan senkronize edin; örneğin ilkel muteksler, Lock
nesneler vb. kullanarak .HashMap
Her iş parçacığı için farklı kullanın . Eğer iş parçacıkları gerçekten ayrık bir anahtar setine sahipse, tek bir Haritayı paylaşmalarına (algoritmik açıdan) gerek olmamalıdır. Aslında, algoritmalarınız bir noktada haritanın anahtarlarını, değerlerini veya girişlerini yineleyen iş parçacıkları içeriyorsa, tek haritayı birden çok haritaya bölmek, işlemenin o kısmı için önemli bir hızlanma sağlayabilir.Bir ConcurrentHashMap kullanın. ConcurrentHashMap, bir kilide itiraz edilme olasılığını azaltmak için bir dizi hash kümesini kapsayan birden fazla kilit kullanır. Tartışmasız bir kilit edinmenin marjinal bir performans etkisi vardır.
Orijinal sorunuzu cevaplamak için: Javadoc'a göre, haritanın yapısı değişmediği sürece, iyisiniz. Bu, hiç öğe kaldırmayacağınız ve haritada bulunmayan yeni anahtarların eklenmeyeceği anlamına gelir. Mevcut anahtarlarla ilişkili değeri değiştirmek sorun değil.
Birden fazla evre eş zamanlı olarak bir karma haritaya erişirse ve iş parçacıklarından en az biri haritayı yapısal olarak değiştirirse, haricen senkronize edilmelidir. (Yapısal bir değişiklik, bir veya daha fazla eşlemeyi 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.)
Görünürlük konusunda hiçbir garanti vermez. Bu nedenle, zaman zaman eski derneklerin alınmasını kabul etmeye istekli olmalısınız.
"Erişim" altında ne demek istediğinize bağlıdır. Sadece okuyorsanız, " önceden olur " kuralları kapsamında verilerin görünürlüğü garanti edildiği sürece aynı anahtarları bile okuyabilirsiniz . Bu, HashMap
herhangi bir okuyucu erişmeye başlamadan önce bunun değişmemesi ve tüm değişikliklerin (ilk yapılar) tamamlanması gerektiği anlamına gelir HashMap
.
A'yı HashMap
herhangi bir şekilde değiştirirseniz , kodunuz basitçe kırılır. @Stephen C, nedenini çok iyi açıklıyor.
DÜZENLEME: İlk durum gerçek durumunuzsa, Collections.unmodifiableMap()
HashMap'inizin asla değişmediğinden emin olmanızı tavsiye ederim . İşaret edilen nesneler de HashMap
değişmemelidir, bu nedenle agresif final
anahtar kelime kullanmak size yardımcı olabilir.
Ve @Lars Andren'in dediği gibi, ConcurrentHashMap
çoğu durumda en iyi seçimdir.
unmodifiableMap
müşterinin haritayı değiştirememesini sağlar. Altta yatan haritanın değişmemesini sağlamak için hiçbir şey yapmaz.
Bir HashMap'i iki iş parçacığından uygun senkronizasyon olmadan değiştirmek, kolayca bir yarış durumuna yol açabilir.
put()
dahili tablonun yeniden boyutlandırılmasına yol açtığında, bu biraz zaman alır ve diğer iş parçacığı eski tabloya yazmaya devam eder.put()
Anahtarların hashcode'ları tablo boyutuna eşit modulo ise, farklı anahtarlar için iki tane aynı paketin güncellenmesine yol açar. (Aslında, karma kod ve paket dizini arasındaki ilişki daha karmaşıktır, ancak yine de çakışmalar olabilir.)HashMap
uygulamanın dahili özelliklerine bağlı olarak, HashMap
veri yapılarında bozulmalar , vb. Nedenlerle bellek anormalliklerine yol açabilirsiniz .