HashMap farklı anahtarlar için güvenli midir?


Yanıtlar:


100

@ 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ığı puttablonun 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ığı putbaş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ığı putanahtarı 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 putveya removetalep eden iki iş parçacığınız varsa, yarış koşulları için çok sayıda fırsat vardır.

Üç çözüm düşünebilirim:

  1. Bir ConcurrentHashMap.
  2. Normal kullanın, HashMapancak dışarıdan senkronize edin; örneğin ilkel muteksler, Locknesneler vb. kullanarak .
  3. HashMapHer 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.

30

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.


6

"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, HashMapherhangi 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ı HashMapherhangi 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 HashMapdeğişmemelidir, bu nedenle agresif finalanahtar kelime kullanmak size yardımcı olabilir.

Ve @Lars Andren'in dediği gibi, ConcurrentHashMapçoğu durumda en iyi seçimdir.


2
ConcurrentHashMap bence en iyi seçimdir. Bunu önermememin tek nedeni, yazarın sormaması :) CAS işlemleri nedeniyle daha az verime sahip, ancak eşzamanlı programlamanın altın kuralının dediği gibi: "Doğru yapın ve ancak o zaman hızlı yapın ":)
Denis Bazhenov

unmodifiableMapmüşterinin haritayı değiştirememesini sağlar. Altta yatan haritanın değişmemesini sağlamak için hiçbir şey yapmaz.
Pete Kirkham

Daha önce de belirttiğim gibi: "HashMap tarafından işaret edilen nesneler de değişmemelidir"
Denis Bazhenov

4

Bir HashMap'i iki iş parçacığından uygun senkronizasyon olmadan değiştirmek, kolayca bir yarış durumuna yol açabilir.

  • Bir 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.)

1
Sadece yarış koşullarından daha kötü. Kullandığınız HashMapuygulamanın dahili özelliklerine bağlı olarak, HashMapveri yapılarında bozulmalar , vb. Nedenlerle bellek anormalliklerine yol açabilirsiniz .
Stephen C
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.