Java'nın JDK'sında eşzamanlı bir Liste var mı?


277

Öğelere dizine göre erişebildiğim eşzamanlı List örneğini nasıl oluşturabilirim? JDK'nın kullanabileceğim herhangi bir sınıfı veya fabrika yöntemi var mı?


28
Neden yapıcı değil? Net'te bulunmayan birkaç CopyOnWriteArrayList önerdi. Her iki sorunun da birbiriyle ilgili olduğunu söyleyebilir, ancak bunu kapatmak için değil !!!
AlikElzin-kilaka

1
Jarrod Roberson'ın neden Stephan tarafından yapılan ayrıntılı düzenlemeleri alıp orijinal, kötü ifade edilmiş soruya geri döndürmenin iyi bir fikir olduğunu düşündüğünü bilmiyorum. Jarrod'un cevabı hala mükemmel bir şekilde kabul edilebilir. Aslında, CopyOnWriteArrayList, JDK'daki tek eşzamanlı sınıf uygulama listesidir. Şaşkın ...
Matt Passell

10
Kabul edilen cevap orijinal soruya olduğu ve Stephan, orijinal posterin soruyu tamamen değiştiren herhangi bir yeri içermediği bir dizi kaynak kodu ile tamamen ilgisiz bir soru sorduğundan List, orijinalin spesifik olandan başka şeyler öneren daha fazla cevap üretti diyor vandalizm olarak kabul edilen bir gerekliliktir . Bir moderatör, cevapların sorunun vandalize edilmiş versiyonuna cevap vermediğinden şikayet eden insanlar nedeniyle soruyu zaten kilitledi.

1
/ locked/ closed/ önceki açıklama

8
Bu sorunun kapatılmasının bir nedeni yok. JDK'da bir kütüphane aramak gibi bir şey olmayan sınıfları soruyor; Java'nın temelidir.
maaartinus

Yanıtlar:


175

Java.util.concurrent içinde eşzamanlı bir liste uygulaması vardır . Özellikle CopyOnWriteArrayList .


84
Listenin tamamını her kesici uçta kopyaladığına dikkat edin, bu nedenle genellikle verimsizdir.
dfrankow

22
@dfrankow Ancak , güncellediğinizden çok daha fazlasını yineliyorsanız daha verimli olabilir.
b1nary.atr0phy

Burada gösterildiği gibi iyi çalışmıyor. Ben sadece onun addAll yöntemini kullanmak ve akışı kullanarak okuyun rağmen istisnalar var. stackoverflow.com/questions/1527519/…
devssh

166

Dizin tabanlı erişime sahip olmak istemiyorsanız ve yalnızca bir Listenin ekleme siparişi koruma özelliklerini istiyorsanız, bir java.util.concurrent.ConcurrentLinkedQueue düşünebilirsiniz . Yinelenebilir uyguladığı için, tüm öğeleri eklemeyi bitirdikten sonra, sözdizimi için gelişmiş özelliğini kullanarak içeriklerin arasında geçiş yapabilirsiniz:

Queue<String> globalQueue = new ConcurrentLinkedQueue<String>();

//Multiple threads can safely call globalQueue.add()...

for (String href : globalQueue) {
    //do something with href
}

4
Bence ( :) için basitleştirilmiş foreach denir: docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
AlikElzin-kilaka 3:14

2
@ AlikElzin-kilaka Haklısın. Sanırım bu isim beni her zaman rahatsız etti, çünkü gerçek sözdizimi "her" kelimesini içermiyor, ancak resmi adı kullanmak için cevabı güncelleyeceğim. :)
Matt Passell

3
@ AlikElzin-kilaka Nitpicking, ancak JLS sürüm 8'e göre "ifade için geliştirilmiş" olarak adlandırılır. Java eğitiminde aynı .
Roland

1
@Roland kesinlikle nitpicking DEĞİLDİR. Java'da "her biri için" ile "için geliştirilmiş" arasında bir fark vardır.
hfontanez

2
@Roland dolaylı olarak. Stream.forEach ve şimdi için gelişmiş olarak bilinen ne arasındaki karışıklığı ortadan kaldırmak için "her döngü için" yeniden "için geliştirilmiş" olarak yeniden adlandırıyorum.
hfontanez

128

İhtiyacınız olan tek şey basit çağırma senkronizasyonu ise Collections.synchronizedList (List) öğesini çok iyi kullanabilirsiniz :

 List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());

70
Sonucu synchronizedList"senkronize", ancak "eşzamanlı" değil. Dizin tabanlı birçok Liste işleminin kendilerinin atomik olmadığı ve daha büyük bir karşılıklı dışlama yapısının parçası olması gerektiği temel bir sorun.

6
IMO, asing a Vectoryerine daha basittir Collections.synchronizedList(new ArrayList<Object>()).
Stephan

8
SyncizedList sonucu "senkronize", ancak "eşzamanlı" değil.
Kanagavelu Sugumar

46

Konumu elde etme ve öğeyi verilen konumdan alma eylemi doğal olarak bir miktar kilitleme gerektirdiğinden (listenin bu iki işlem arasında yapısal değişiklikler yapamazsınız).

Eşzamanlı bir koleksiyon fikri, her işlemin kendi başına atomik olması ve açık kilitleme / senkronizasyon olmadan yapılabilmesidir.

Bu nedenle elementi atomik bir işlem olarak nbelirli bir konumdan almak, Listeşzamanlı erişimin beklendiği bir durumda çok mantıklı değildir.


6
Joachim, bence kafasına çiviyi vurdun. Örneğin, eşzamanlı liste olarak salt okunur bir listeyi ele alalım. Elemanı listeden N konumuna getirmek sadece mantıklı değil, aynı zamanda problemin özetidir. Dolayısıyla, değişmez bir liste (küçük L harfi) iyi bir örnek olabilir, ancak bir Liste (büyük L harfi) değildir. CopyOnWriteArrayList eşzamanlıdır, ancak birçok kişi performansı beğenmez. Halat çizgileri (ip ipleri) boyunca bir çözüm muhtemelen iyi bir kazanan olacaktır.
johnstosh

1
Çok iyi bir nokta. Ancak OP'nin kullanacağı Liste çok özel bir kullanıma sahip olabilir. Örneğin, eşzamanlı bir ortamda doldurulabilir, sonra "kilitlenir" (ne anlama gelirse) ve daha sonra indeksle güvenli bir şekilde erişilebilir. Bu nedenle, böyle bir Listeyi doldurmanın ilk aşamasında, yine de iş parçacığı için güvenli bir uygulama gerekir. Ne yazık ki, OP aradığı Listenin nasıl kullanılacağı konusunda spesifik değildi.
igor.zh

13

Şu seçeneklere sahipsiniz:

  • Collections.synchronizedList(): Herhangi sarar Listuygulaması ( ArrayList, LinkedListveya 3. parti listesi). Her yönteme erişim (okuma ve yazma) kullanılarak korunacaktır synchronized. iterator()Döngü için kullanılırken veya geliştirilirken, el ile eşitlemeniz gerekir; yinelemede iken, diğer iş parçacıklarının okunması bile tamamen engellenir. Her biri hasNextve nextaramalar için ayrı ayrı senkronize edebilirsiniz , ancak daha sonra ConcurrentModificationExceptionmümkündür.

  • CopyOnWriteArrayList: Değiştirilmesi pahalı, ancak okumak için ücretsiz. Yineleyiciler asla atmazlar ConcurrentModificationException, liste yineleme sırasında başka bir iş parçacığı tarafından değiştirilse bile yineleyici oluşturulduğu anda listenin bir anlık görüntüsünü döndürürler. Nadiren güncellenen listeler için kullanışlıdır. addAllGüncellemeler için toplu işlemler tercih edilir - dahili dizi daha az defa kopyalanır.

  • Vector: çok benzer synchronizedList, ancak yineleme de senkronize edilir. Ancak, ConcurrentModificationExceptionvektör yinelemede başka bir iş parçacığı tarafından değiştirilirse , yineleyiciler fırlatabilir .

Diğer seçenekler:

  • Collections.unmodifiableList(): kilitsiz, diş güvenli, ancak değiştirilemez
  • Queueveya Dequeyalnızca listenin sonuna ekler / kaldırır ve listeyi tekrarlarsanız alternatif olabilir. Dizine göre erişim yok ve rastgele yerlerde ekleme / çıkarma işlemi yok. Daha iyi performans ve daha iyi eşzamanlı erişim ile birden fazla eşzamanlı uygulamaları vardır, ancak bu sorunun kapsamı dışındadır. JCTools'a da göz atabilirsiniz , bunlar tek tüketici veya tek üretici için uzmanlaşmış daha performanslı kuyruk uygulamaları içerir.

4

resim açıklamasını buraya girin

CopyOnWriteArrayList, tüm değişken işlemlerin (ekleme, ayarlama vb.) Temel dizinin yeni bir kopyasını oluşturarak uygulandığı ArrayList'in iş parçacığı için güvenli bir çeşididir.

CopyOnWriteArrayList senkronize Liste uygulayan Liste arabiriminin eşzamanlı bir alternatifidir ve java.util.concurrent paketinin bir parçasıdır ve iş parçacığı için güvenli bir koleksiyondur.

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

CopyOnWriteArrayList hata korumalıdır ve Yineleme sırasında temel CopyOnWriteArrayList değiştirilirken ConcurrentModificationException öğesini atmaz ArrayList'in ayrı bir kopyasını kullanın.

Kopyalama dizisi, klonlanmış bir kopya oluşturulacak her güncelleme işlemini içerdiğinden, bu genellikle çok maliyetlidir. CopyOnWriteArrayList yalnızca sık okuma işlemi için en iyi seçimdir.

/**
         * Returns a shallow copy of this list.  (The elements themselves
         * are not copied.)
         *
         * @return a clone of this list
         */
        public Object clone() {
            try {
                @SuppressWarnings("unchecked")
                CopyOnWriteArrayList<E> clone =
                    (CopyOnWriteArrayList<E>) super.clone();
                clone.resetLock();
                return clone;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError();
            }
        }
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.