2010-03-14 6 views
68

Ich habe eine ArrayList, die ich verwenden möchte, um RaceCar-Objekte zu halten, die die Thread-Klasse erweitern, sobald sie mit der Ausführung fertig sind. Eine Klasse namens Race behandelt diese ArrayList mit einer Callback-Methode, die das RaceCar-Objekt aufruft, wenn die Ausführung abgeschlossen ist. Die Callback-Methode addFinisher (RaceCar-Finisher) fügt der ArrayList das RaceCar-Objekt hinzu. Dies soll die Reihenfolge angeben, in der die Threads die Ausführung beenden.Wie mache ich meine ArrayList Thread-Safe? Ein anderer Ansatz für Probleme in Java?

Ich weiß, dass ArrayList nicht synchronisiert ist und somit nicht Thread-sicher ist. Ich habe versucht, die Collections.synchronizedCollection (c Collection) -Methode zu verwenden, indem ich eine neue ArrayList übergeben und die zurückgegebene Collection einer ArrayList zuweisen. Aber das gibt mir einen Compiler-Fehler:

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

Hier ist der relevante Code:

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); 
    } 

Was ich wissen muß, ist, bin ich einen richtigen Ansatz, und wenn nicht, was soll ich verwenden um meinen Code threadsicher zu machen? Danke für die Hilfe!

+2

(Hinweis , die 'List'-Schnittstelle ist nicht wirklich vollständig genug, um beim Multithreading sehr nützlich zu sein.) –

+3

Ich möchte nur darauf hinweisen, dass wir ohne 'Collections.synchronizedList()' hier eine ECHTE Race Condition haben: P –

Antwort

106

Verwenden Sie Collections.synchronizedList().

Ex:

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

Danke! Ich bin mir nicht sicher, warum ich nicht daran gedacht habe, einen Vektor zu benutzen, seit ich mich erinnere, irgendwo zu lesen, wo sie synchronisiert wurden. – ericso

+29

Vielleicht ist es keine gute Idee, mit Klassen zu arbeiten, die als veraltet definiert sind. – frandevel

+1

Obwohl Vector ziemlich alt ist und keine Collections-Unterstützung bietet, ist es nicht veraltet. Es ist wahrscheinlich besser, Collections.synchronizedList() zu verwenden, wie andere Leute hier sagten. – Asturio

33

ändern

private ArrayList finishingOrder; 

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

zu

private List finishingOrder; 

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

Liste ist ein übergeordneter Typ von Arraylist, so dass Sie das angeben müssen.

Sonst scheint das, was Sie tun, in Ordnung. Andere Option ist, dass Sie Vector verwenden können, der synchronisiert wird, aber das ist wahrscheinlich, was ich tun würde.

+1

Oder 'List' wäre wahrscheinlich nützlicher. Oder 'Liste '. –

+0

Guter Punkt, machen Sie es privat Liste finishingOrder = Collections.synchronizedList (...) –

+0

Ich versuchte dies und der Compiler beschwere sich jetzt über mich Aufruf ArrayList-Methoden auf einer Sammlung: ' ' // Drucken Gewinner System.out.println ("Der Gewinner ist" + ((RaceCar) finishingOrder.get (0)). ToString() + "!"); ' Es heißt, dass die Methode get (0) nicht gefunden wurde. Gedanken? – ericso

0

Sie können von Arraylist Vektortyp ändern, in dem jedes Verfahren synchronisiert ist.

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

Wenn Sie vorschlagen, eine andere Sammlung zu verwenden, ist Vector wahrscheinlich eine schlechte Wahl. Es handelt sich um eine Legacy-Sammlung, die beim Design des neuen Java Collections Framework nachgerüstet wurde. Ich bin sicher, es gibt bessere Möglichkeiten im java.until.concurrent-Paket. –

5

Sie könnte sein, den falschen Ansatz. Nur weil ein Thread, der ein Auto simuliert, vor einem anderen Auto-Simulations-Thread endet, bedeutet das nicht, dass der erste Thread das simulierte Rennen gewinnen sollte.

Es hängt viel von Ihrer Anwendung ab, aber es könnte besser sein, einen Thread zu haben, der den Zustand aller Autos in kleinen Zeitabständen bis zum Ende des Rennens berechnet. Oder, wenn Sie mehrere Threads verwenden möchten, können Sie jedes Auto die "simulierte" Zeit aufzeichnen, die es brauchte, um das Rennen zu beenden, und den Gewinner als den mit der kürzesten Zeit auswählen.

+0

Das ist ein guter Punkt. Dies ist nur eine Übung aus einem Text, mit dem ich Java lerne. Der Punkt war, zu lernen, wie man Threads verwendet, und ich gehe tatsächlich über die ursprünglichen Spezifikationen des Problems hinaus, indem ich einen Mechanismus erstelle, um die Gewinner zu protokollieren.Ich habe einen Timer benutzt, um die Gewinner zu messen. Aber ehrlich, ich glaube, ich habe bekommen, was ich von der Übung brauche. – ericso

2

Sie können auch synchronized Schlüsselwort für addFinisher Verfahren wie dieses

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

verwenden So können Sie Arraylist-Methode Thread-sicher mit dieser Art und Weise verwenden hinzuzufügen.

+3

gut, aber was ist, wenn Sie zwei Methoden haben: addFinisher und delFinisher? Beide Methoden sind Thread-sicher, aber da beide auf die gleiche ArrayList zugreifen, würden Sie immer noch Probleme bekommen. – masi

+0

@masi Dann synchronisierst du einfach jedes Mal auf ein 'final Object', wenn du auf irgendeine Weise auf' Collection' zugreifst. – mkuech

6

CopyOnWriteArrayList

Verwenden CopyOnWriteArrayList Klasse. Dies ist die Thread-sichere Version von ArrayList.

+2

Denken Sie zweimal, wenn Sie diese Klasse betrachten. Um das Klassendokument zu zitieren: "Dies ist normalerweise zu kostspielig, aber es kann effizienter sein als Alternativen, wenn Traversierungsoperationen Mutationen zahlenmäßig weit überlegen sind, und ist nützlich, wenn Sie Traversalen nicht synchronisieren können oder wollen, aber Interferenzen zwischen gleichzeitigen Threads ausschließen müssen . "Siehe auch [Unterschied zwischen CopyOnWriteArrayList und synchronizedList] (http://stackoverflow.com/q/28979488/642706) –

+0

Diese Klasse kommt ins Spiel, wenn Sie die Liste nur selten ändern, aber oft über die Elemente iterieren. z.B. wenn Sie eine Gruppe von Zuhörern haben. Sie registrieren sie und dann iterieren Sie sehr viel ..., wenn Sie die Listenschnittstelle nicht explizit benötigen, aber Operationen so ändern und lesen, dass sie gleichzeitig ablaufen, sollten Sie 'ConcurrentLinkedQueue' berücksichtigen – benez

0

Wann immer Sie eine ant-thread-sichere Version des ant-Sammlungsobjekts verwenden möchten, nehmen Sie die Hilfe java.util.concurrent. * Paket. Es hat fast alle gleichzeitige Version von unsynchronisierten Auflistungsobjekten. zB: für ArrayList, haben Sie java.util.concurrent.CopyOnWriteArrayList

Sie können Collections.synchronizedCollection (jedes Sammlungsobjekt) tun, aber erinnern Sie sich an diese klassische Synchronisation. Technik ist teuer und kommt mit Overhead-Aufwand. java.util.concurrent. * Paket ist weniger teuer und die Leistung in besserem Weg verwalten mithilfe von Mechanismen wie

copy-on-write,compare-and-swap,Lock,snapshot iterators,etc.

Also, lieber etwas von java.util.concurrent. * Paket

Verwandte Themen