ArrayList'imi Nasıl İş Parçacığı İçin Güvenli hale getirebilirim? Java'daki soruna başka bir yaklaşım?


91

Yürütme biter bitmez Thread sınıfını genişleten RaceCar nesnelerini tutmak için kullanmak istediğim bir ArrayList var. Race adlı bir sınıf, bu ArrayList'i, RaceCar nesnesinin yürütülmesi bittiğinde çağırdığı bir geri çağrı yöntemini kullanarak işler. Geri çağrı yöntemi addFinisher (RaceCar sonlandırıcı), RaceCar nesnesini ArrayList'e ekler. Bunun, Threads'ın çalışmayı bitirdiği sırayı vermesi gerekiyor.

ArrayList'in senkronize olmadığını ve bu nedenle iş parçacığı için güvenli olmadığını biliyorum. Yeni bir ArrayList içinde geçerek ve döndürülen Koleksiyonu bir ArrayList'e atayarak Collections.synchronizedCollection (c Collection) yöntemini kullanmayı denedim. Ancak, bu bana bir derleyici hatası veriyor:

Race.java:41: incompatible types
found   : java.util.Collection
required: java.util.ArrayList
finishingOrder = Collections.synchronizedCollection(new ArrayList(numberOfRaceCars));

İşte ilgili kod:

public class Race implements RaceListener {
    private Thread[] racers;
    private ArrayList finishingOrder;

    //Make an ArrayList to hold RaceCar objects to determine winners
    finishingOrder = Collections.synchronizedCollection(new ArrayList(numberOfRaceCars));

    //Fill array with RaceCar objects
    for(int i=0; i<numberOfRaceCars; i++) {
    racers[i] = new RaceCar(laps, inputs[i]);

        //Add this as a RaceListener to each RaceCar
        ((RaceCar) racers[i]).addRaceListener(this);
    }

    //Implement the one method in the RaceListener interface
    public void addFinisher(RaceCar finisher) {
        finishingOrder.add(finisher);
    }

Bilmem gereken şey, doğru bir yaklaşım mı kullanıyorum ve kullanmıyorum, kodumu iş parçacığı için güvenli hale getirmek için ne kullanmalıyım? Yardım için teşekkürler!


2
(Unutmayın, Listarayüz çoklu iş parçacığında çok yararlı olacak kadar tam değildir.)
Tom Hawtin - tackline

3
Sadece şunu belirtmek isterim ki, olmasaydı Collections.synchronizedList(), burada GERÇEK bir yarış durumumuz olurdu: P
Dylan Watson

Yanıtlar:


149

Kullanın Collections.synchronizedList().

Ör:

Collections.synchronizedList(new ArrayList<YourClassNameHere>())

2
Teşekkürler! Senkronize oldukları bir yerde okuduğumu hatırladığım için neden sadece bir Vector kullanmayı düşünmediğimden emin değilim.
ericso

32
Belki de kullanımdan kaldırılmış olarak tanımlanan sınıflarla çalışmak iyi bir fikir değildir
frandevel

1
Vector oldukça eski olmasına ve Koleksiyon desteğinden yoksun olmasına rağmen, kullanımdan kaldırılmış değildir. Burada başkalarının söylediği gibi Collections.synchronizedList () kullanmak muhtemelen daha iyidir.
Asturio

14
-1 yorumlar için. Vector kullanımdan kaldırılmadı ve nasıl koleksiyon desteği yok? List'i uygular. Vector için javadoc özellikle şunu söylüyor: "Java 2 platformu v1.2'den itibaren, bu sınıf, List arayüzünü uygulamak için güçlendirildi ve bu, onu Java Koleksiyonları Çerçevesinin bir üyesi yaptı. Yeni koleksiyon uygulamalarının aksine, Vector senkronize edildi." Vector kullanmamak için iyi nedenler olabilir (senkronizasyondan kaçınmak, uygulamaları değiştirmek), ancak "eski" veya "modern olmamak" bunlardan biri değildir.
fool4jesus

1
Aşağıdaki yöntemleri kullanın: Collections.synchronizedList (list); Collections.synchronizedSet (set); Collections.synchronizedMap (harita); Yukarıdaki yöntemler, koleksiyonu parametre olarak alır ve senkronize edilmiş ve iş parçacığı açısından güvenli olan aynı türden koleksiyonları döndürür.
Sameer Kazi

35

Değişiklik

private ArrayList finishingOrder;

//Make an ArrayList to hold RaceCar objects to determine winners
finishingOrder = Collections.synchronizedCollection(new ArrayList(numberOfRaceCars)

-e

private List finishingOrder;

//Make an ArrayList to hold RaceCar objects to determine winners
finishingOrder = Collections.synchronizedList(new ArrayList(numberOfRaceCars)

List, ArrayList'in bir süper tipidir, bu yüzden bunu belirtmeniz gerekir.

Aksi takdirde, yaptığınız şey iyi görünüyor. Diğer bir seçenek ise, senkronize olan Vector'u kullanabilmenizdir, ancak muhtemelen yapacağım şey budur.


1
Ya Listda muhtemelen daha yararlı olur. Veya List<RaceCar>.
Tom Hawtin - tackline

İyi bir nokta, özel yap Liste finishingOrder = Collections.synchronizedList (...)
Reverend Gonzo

Bunu denedim ve derleyici şimdi bir Koleksiyonda ArrayList yöntemlerini çağırmamdan şikayet ediyor: //Print out winner System.out.println("The Winner is " + ((RaceCar) finishingOrder.get(0)).toString() + "!"); get (0) yönteminin bulunmadığını söylüyor. Düşünceler?
ericso

Yorumumu silip yeniden eklediğim için üzgünüm. Ters işaret kullanarak çalışmak için vurgulamayı almaya çalışıyordum. Bu tür konularda OKB alıyorum.
ericso

Hayır, bu işe yaramıyor. Koleksiyonu bir Listeye yayınlamaz: Race.java:41: bulunan uyumsuz türler: java.util.Collection gerekli: java.util.List finishingOrder = Collections.synchronizedCollection (new ArrayList (numberOfRaceCars));
ericso

12

CopyOnWriteArrayList

CopyOnWriteArrayListSınıfı kullan . Bu, iş parçacığı açısından güvenli sürümüdür ArrayList.


5
Bu dersi düşünürken iki kez düşünün. Sınıf belgesini alıntılamak için: "Bu normalde çok maliyetlidir, ancak geçiş işlemleri mutasyonları büyük ölçüde aştığında alternatiflerden daha verimli olabilir ve çapraz geçişleri senkronize edemediğinizde veya istemediğinizde, ancak eşzamanlı iş parçacıkları arasında girişimi engellemeniz gerektiğinde kullanışlıdır. . " Ayrıca, CopyOnWriteArrayList ile synchronizedList
Basil Bourque

1
bu sınıf, listeyi nadiren değiştirdiğinizde, ancak genellikle öğeler üzerinde yinelediğinizde devreye girer. örneğin bir dizi dinleyiciniz olduğunda. onları kaydedersiniz ve sonra çok fazla ConcurrentLinkedQueue
yinelersiniz

7

Sen belki yanlış bir yaklaşım kullanıyor. Bir arabayı simüle eden bir iş parçacığının başka bir araba simülasyonu iş parçacığından önce bitmesi, ilk iş parçacığının simüle edilmiş yarışı kazanması gerektiği anlamına gelmez.

Bu, uygulamanıza büyük ölçüde bağlıdır, ancak yarış tamamlanana kadar küçük zaman aralıklarında tüm arabaların durumunu hesaplayan bir iş parçacığına sahip olmak daha iyi olabilir. Ya da, birden fazla iş parçacığı kullanmayı tercih ederseniz, her arabanın yarışı tamamlamak için geçen "simüle edilmiş" süreyi kaydetmesini ve kazananı en kısa süreye sahip olanı olarak seçmesini sağlayabilirsiniz.


İyi bir noktaya değindin. Bu sadece Java öğrenmek için kullandığım bir metinden bir alıştırma. Önemli olan konu başlıklarının nasıl kullanılacağını öğrenmekti ve aslında kazananları kaydetmek için bir mekanizma oluştururken sorunun orijinal özelliklerinin ötesine geçiyorum. Kazananları ölçmek için bir zamanlayıcı kullanmayı düşündüm. Ama dürüst olmak gerekirse, ihtiyacım olanı egzersizden aldığımı düşünüyorum.
ericso

5

Bunun gibi bir yöntem synchronizediçin anahtar kelimeyi de kullanabilirsinizaddFinisher

    //Implement the one method in the RaceListener interface
    public synchronized void addFinisher(RaceCar finisher) {
        finishingOrder.add(finisher);
    }

Böylece ArrayList add yöntemini bu yolla iş parçacığı açısından güvenli kullanabilirsiniz.


4
peki, peki ya iki yönteminiz varsa: addFinisher ve delFinisher? Her iki yöntem de iş parçacığı açısından güvenlidir, ancak her ikisi de aynı ArrayList'e eriştiği için yine de sorun yaşarsınız.
omni

1
@masi Daha sonra , herhangi bir şekilde final Objecther eriştiğinizde bunun yerine bir üzerinde eşitleme yaparsınız Collection.
mkuech

2

Ant collection nesnesinin ant thread güvenli sürümünü kullanmak istediğinizde, java.util.concurrent. * Paketinden yardım alın . Eşzamanlı olmayan koleksiyon nesnelerinin neredeyse tüm eşzamanlı sürümlerine sahiptir. örneğin: ArrayList için java.util.concurrent.CopyOnWriteArrayList var

Collections.synchronizedCollection (herhangi bir koleksiyon nesnesi) yapabilirsiniz, ancak bu klasik senkronizasyonu unutmayın. teknik pahalıdır ve performans yükü ile birlikte gelir. java.util.concurrent. * paketi daha ucuzdur ve performansı aşağıdaki gibi mekanizmalar kullanarak daha iyi yönetir:

yazma üzerine kopyalama, karşılaştırma ve değiştirme, Kilitleme, anlık görüntü yineleyiciler vb.

Bu nedenle, java.util.concurrent. * Paketinden bir şey tercih edin


1

Bunun yerine Vektör olarak da kullanabilirsiniz, çünkü vektörler iş parçacığı açısından güvenli ve dizi listesi değildir. Vektörler eski olsalar da amacınızı kolayca çözebilirler.

Ancak Dizi Listenizi aşağıdaki kod gibi senkronize edebilirsiniz:

Collections.synchronizedList(new ArrayList(numberOfRaceCars())); 

-1

ArrayList'ten, her yöntemin senkronize edildiği Vektör türüne geçebilirsiniz.

private Vector finishingOrder;
//Make a Vector to hold RaceCar objects to determine winners
finishingOrder = new Vector(numberOfRaceCars);

5
Başka bir koleksiyon kullanmayı önerecekseniz, muhtemelen Vector kötü bir seçimdir. Yeni Java Koleksiyonları Çerçevesinin tasarımına uyarlanmış eski bir koleksiyondur. Java.until.concurrent paketinde daha iyi seçenekler olduğundan eminim.
Edwin Dalorzo
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.