121

I have an ArrayList that I want to use to hold RaceCar objects that extend the Thread class as soon as they are finished executing. A class, called Race, handles this ArrayList using a callback method that the RaceCar object calls when it is finished executing. The callback method, addFinisher(RaceCar finisher), adds the RaceCar object to the ArrayList. This is supposed to give the order in which the Threads finish executing.

I know that ArrayList isn't synchronized and thus isn't thread-safe. I tried using the Collections.synchronizedCollection(c Collection) method by passing in a new ArrayList and assigning the returned Collection to an ArrayList. However, this gives me a compiler error:

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

Here is the relevant 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);
    }

What I need to know is, am I using a correct approach and if not, what should I use to make my code thread-safe? Thanks for the help!

ericso
  • 3,218
  • 7
  • 29
  • 36

8 Answers8

190

Use Collections.synchronizedList().

Ex:

Collections.synchronizedList(new ArrayList<YourClassNameHere>())
rogerdpack
  • 62,887
  • 36
  • 269
  • 388
Amir Afghani
  • 37,814
  • 16
  • 84
  • 124
  • 2
    Thanks! I'm not sure why I didn't think to just use a Vector since I remember reading somewhere they were synchronized. – ericso Mar 14 '10 at 23:12
  • 1
    Although Vector is quite old and lacks Collections-support, it is not deprecated. It is probably better to use Collections.synchronizedList() like other people said here. – Asturio Jul 04 '13 at 10:09
  • 20
    -1 for comments. Vector is not deprecated and how does it not have collections support? It implements List. The javadoc for Vector specifically says: "As of the Java 2 platform v1.2, this class was retrofitted to implement the List interface, making it a member of the Java Collections Framework. Unlike the new collection implementations, Vector is synchronized." There may be good reasons for not using Vector (avoiding synchronization, changing out implementations), but being "obsolete" or "not modern" isn't one of them. – fool4jesus Oct 01 '13 at 15:32
  • 1
    Use below methods: Collections.synchronizedList(list); Collections.synchronizedSet(set); Collections.synchronizedMap(map); Above methods take collection as parameter and return same type of collection which are synchronized and thread safe. – Sameer Kazi Feb 25 '15 at 06:33
  • 3
    Comments are irrelevant as answer has been edited and is not suggesting `Vector` anymore. – Koray Tugay Jan 29 '21 at 20:19
37

Change

private ArrayList finishingOrder;

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

to

private List finishingOrder;

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

List is a supertype of ArrayList so you need to specify that.

Otherwise, what you're doing seems fine. Other option is you can use Vector, which is synchronized, but this is probably what I would do.

Reverend Gonzo
  • 39,701
  • 6
  • 59
  • 77
  • 1
    Or `List` would probably be more useful. Or `List`. – Tom Hawtin - tackline Mar 14 '10 at 22:24
  • Good point, make it private List finishingOrder = Collections.synchronizedList(...) – Reverend Gonzo Mar 14 '10 at 23:00
  • I tried this and the compiler is now complaining about me calling ArrayList methods on a Collection: `//Print out winner System.out.println("The Winner is " + ((RaceCar) finishingOrder.get(0)).toString() + "!");` It is saying the get(0) method is not found. Thoughts? – ericso Mar 14 '10 at 23:01
  • Sorry about deleting and re-adding my comment. I was trying to get the highlighting to work using backticks. I get OCD about that kind of stuff. – ericso Mar 14 '10 at 23:02
  • No, that doesn't work. It won't cast the Collection to a List: Race.java:41: incompatible types found : java.util.Collection required: java.util.List finishingOrder = Collections.synchronizedCollection(new ArrayList(numberOfRaceCars)); – ericso Mar 14 '10 at 23:06
28

CopyOnWriteArrayList

Use CopyOnWriteArrayList class. This is the thread safe version of ArrayList.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Piyush Singh
  • 533
  • 5
  • 17
  • 13
    Think twice when considering this class. To quote the class doc: ”This is ordinarily too costly, but may be more efficient than alternatives when traversal operations vastly outnumber mutations, and is useful when you cannot or don't want to synchronize traversals, yet need to preclude interference among concurrent threads.” Also, see [Difference between CopyOnWriteArrayList and synchronizedList](http://stackoverflow.com/q/28979488/642706) – Basil Bourque Oct 29 '16 at 03:48
  • 3
    this class comes into play when you rarely modify the list, but often iterate over the elements. e.g. when you have a set of listeners. you register them and then you iterate a lot..., if you don't explicitly need the list interface, but modify and read operations to be concurrent, consider `ConcurrentLinkedQueue` – benez Nov 26 '16 at 11:36
7

You can also use synchronized keyword for addFinisher method like this

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

So you can use ArrayList add method thread-safe with this way.

erhun
  • 3,549
  • 2
  • 35
  • 44
  • 4
    well, but what if you got two methods: addFinisher and delFinisher? Both methods are thread-safe but since both access the same ArrayList you would still get trouble. – omni Jun 26 '14 at 20:40
  • 1
    @masi Then you just synchronize on a `final Object` instead every time you access the `Collection` in any way. – mkuech Nov 04 '15 at 18:23
7

You might be using the wrong approach. Just because one thread that simulates a car finishes before another car-simulation thread doesn't mean that the first thread should win the simulated race.

It depends a lot on your application, but it might be better to have one thread that computes the state of all cars at small time intervals until the race is complete. Or, if you prefer to use multiple threads, you might have each car record the "simulated" time it took to complete the race, and choose the winner as the one with shortest time.

erickson
  • 265,237
  • 58
  • 395
  • 493
  • That's a good point. This is just an exercise from a text I'm using to learn Java. The point was to learn how to use threads and I'm actually going beyond the original specifications of the problem in building a mechanism to log the winners. I though about using a timer to measure the winners. But honestly, I think I've gotten what I need from the exercise. – ericso Mar 15 '10 at 14:15
2

Whenever you want to use ant thread safe version of ant collection object,take help of java.util.concurrent.* package. It has almost all concurrent version of unsynchronized collection objects. eg: for ArrayList, you have java.util.concurrent.CopyOnWriteArrayList

You can do Collections.synchronizedCollection(any collection object),but remember this classical synchr. technique is expensive and comes with performence overhead. java.util.concurrent.* package is less expensive and manage the performance in better way by using mechanisms like

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

So,Prefer something from java.util.concurrent.* package

2

You can also use as Vector instead, as vectors are thread safe and arraylist are not. Though vectors are old but they can solve your purpose easily.

But you can make your Arraylist synchronized like code given this:

Collections.synchronizedList(new ArrayList(numberOfRaceCars())); 
Lovepreet Singh
  • 4,792
  • 1
  • 18
  • 36
Naman jain
  • 66
  • 11
-2

You can change from ArrayList to Vector type, in which every method is synchronized.

private Vector finishingOrder;
//Make a Vector to hold RaceCar objects to determine winners
finishingOrder = new Vector(numberOfRaceCars);
darlinton
  • 2,131
  • 16
  • 21
  • 6
    If you are going to suggest using another collection, probably Vector is a poor choice. It is a legacy collection which was retrofitted to the design of the new Java Collections Framework. I am sure there are better choices in the java.until.concurrent package. – Edwin Dalorzo Mar 08 '14 at 15:13