2010-03-14 4 views
68

J'ai une ArrayList que je veux utiliser pour contenir des objets RaceCar qui étendent la classe Thread dès qu'ils sont terminés. Une classe, appelée Race, gère cette ArrayList à l'aide d'une méthode de rappel que l'objet RaceCar appelle lorsque l'exécution est terminée. La méthode de rappel, addFinisher (finisseur RaceCar), ajoute l'objet RaceCar à ArrayList. Ceci est supposé donner l'ordre dans lequel les Threads finissent d'être exécutés.Comment rendre mon ArrayList thread-safe? Une autre approche du problème en Java?

Je sais que ArrayList n'est pas synchronisé et n'est donc pas sûr pour les threads. J'ai essayé d'utiliser la méthode Collections.synchronizedCollection (c Collection) en transmettant une nouvelle ArrayList et en affectant la Collection retournée à une ArrayList. Cependant, cela me donne une erreur de compilation:

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

Voici le code correspondant:

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

Ce que je dois savoir est, que j'utilise une approche correcte et sinon, que dois-je utiliser pour rendre mon code thread-safe? Merci pour l'aide!

+2

(Note , l'interface 'List' n'est pas vraiment assez complète pour être très utile dans le multithreading.) –

+3

Je voudrais juste souligner que, sans' Collections.synchronizedList() ', nous aurions une condition de concurrence REAL ici: P –

Répondre

106

Utilisez Collections.synchronizedList().

Ex:

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

Merci! Je ne sais pas pourquoi je n'ai pas pensé à utiliser un vecteur puisque je me souviens avoir lu quelque part qu'ils étaient synchronisés. – ericso

+29

Peut-être que ce n'est pas une bonne idée de travailler avec des classes définies comme obsolètes – frandevel

+1

Bien que Vector soit assez ancien et ne soit pas compatible avec Collections, il n'est pas obsolète. Il est probablement préférable d'utiliser Collections.synchronizedList() comme d'autres personnes l'ont dit ici. – Asturio

33

changement

private ArrayList finishingOrder; 

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

à

private List finishingOrder; 

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

Liste est un super-type de ArrayList donc vous devez le préciser.

Sinon, ce que vous faites semble bien. Une autre option est que vous pouvez utiliser Vector, qui est synchronisé, mais c'est probablement ce que je ferais.

+1

Ou 'List' serait probablement plus utile. Ou 'Liste '. –

+0

Bon point, rendez-le privé Liste finishingOrder = Collections.synchronizedList (...) –

+0

J'ai essayé ceci et le compilateur se plaint maintenant de moi appelant les méthodes ArrayList sur une collection: '// Print out winner System.out.println ("Le gagnant est" + ((RaceCar) finishingOrder.get (0)). ToString() + "!"); ' Il dit que la méthode get (0) est introuvable. Pensées? – ericso

0

Vous pouvez changer de type ArrayList vecteur, dans lequel chaque méthode est synchronisée.

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

Si vous allez suggérer d'utiliser une autre collection, le vecteur est probablement un mauvais choix. C'est une collection héritée qui a été adaptée à la conception du nouveau framework de collections Java. Je suis sûr qu'il y a de meilleurs choix dans le paquetage java.until.concurrent. –

5

Vous pourriez être en utilisant la mauvaise approche. Tout simplement parce qu'un fil qui simule une voiture finit avant un autre thread de simulation de voiture ne signifie pas que le premier fil devrait gagner la course simulée.

Cela dépend beaucoup de votre application, mais il peut être préférable d'avoir un thread qui calcule l'état de toutes les voitures à de petits intervalles de temps jusqu'à la fin de la course. Ou, si vous préférez utiliser plusieurs threads, vous pouvez demander à chaque voiture d'enregistrer le temps «simulé» nécessaire pour terminer la course, et choisir le vainqueur comme étant le plus court.

+0

C'est un bon point. Ceci est juste un exercice à partir d'un texte que j'utilise pour apprendre Java. Le but était d'apprendre à utiliser les threads et je vais en fait au-delà des spécifications initiales du problème en construisant un mécanisme pour connecter les gagnants.Je pensais à utiliser une minuterie pour mesurer les gagnants. Mais honnêtement, je pense que j'ai obtenu ce dont j'ai besoin de l'exercice. – ericso

2

Vous pouvez également utiliser synchronized mot-clé pour addFinisher méthode comme celle

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

avec cette façon, vous pouvez donc utiliser la méthode ArrayList ajouter thread-safe.

+3

bien, mais si vous avez deux méthodes: addFinisher et delFinisher? Les deux méthodes sont thread-safe mais puisque les deux accèdent au même ArrayList vous auriez toujours des problèmes. – masi

+0

@masi Ensuite, vous ne faites que synchroniser sur un 'Object final' à chaque fois que vous accédez au' Collection' de quelque façon que ce soit. – mkuech

6

CopyOnWriteArrayList

Utilisez la classe CopyOnWriteArrayList. Ceci est la version sans risque de thread de ArrayList.

+2

Réfléchissez à deux fois en considérant cette classe. Pour citer le doc de classe: «Ceci est généralement trop coûteux, mais peut être plus efficace que les alternatives lorsque les opérations de traversée surpassent largement les mutations, et est utile lorsque vous ne pouvez pas synchroniser les traversées. En outre, voir [Différence entre CopyOnWriteArrayList et synchronizedList] (http://stackoverflow.com/q/28979488/642706) –

+0

cette classe entre en jeu lorsque vous modifiez rarement la liste, mais que vous parcourez souvent les éléments. par exemple. quand vous avez un ensemble d'auditeurs. vous les enregistrez et ensuite vous itérez beaucoup ..., si vous n'avez pas explicitement besoin de l'interface list, mais modifiez et lisez les opérations pour être simultanées, considérez 'ConcurrentLinkedQueue' – benez

0

Chaque fois que vous voulez utiliser du fil fourmi version sécurisée de l'objet de collection de fourmis, prendre l'aide de java.util.concurrent. * Package. Il a presque toute la version simultanée d'objets de collection non synchronisés. Par exemple: pour ArrayList, vous avez java.util.concurrent.CopyOnWriteArrayList

Vous pouvez faire Collections.synchronizedCollection (n'importe quel objet de collection), mais souvenez-vous de ce synchr. La technique est coûteuse et vient avec des frais généraux de performance. java.util.concurrent. * Forfait est moins cher et gérer les performances d'une meilleure manière en utilisant des mécanismes comme

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

Alors, préférez quelque chose de java.util.concurrent. * Package