How do I make my ArrayList Thread-Safe? Another approach to problem in Java?
Asked Answered
L

8

124

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!

Latialatices answered 14/3, 2010 at 22:18 Comment(3)
(Note, the List interface isn't really complete enough to be very useful in multithreading.)Arbogast
I'd just like to point out that, without Collections.synchronizedList(), we'd have a REAL race condition here :PPostfix
Check this link programmerzdojo.com/java-tutorials/…Expedition
P
193

Use Collections.synchronizedList().

Ex:

Collections.synchronizedList(new ArrayList<YourClassNameHere>())
Prod answered 14/3, 2010 at 23:4 Comment(5)
Thanks! I'm not sure why I didn't think to just use a Vector since I remember reading somewhere they were synchronized.Latialatices
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.Okajima
-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.Ula
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.Debose
Comments are irrelevant as answer has been edited and is not suggesting Vector anymore.Hierarch
R
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.

Richardson answered 14/3, 2010 at 22:20 Comment(5)
Or List would probably be more useful. Or List<RaceCar>.Arbogast
Good point, make it private List finishingOrder = Collections.synchronizedList(...)Richardson
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?Latialatices
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.Latialatices
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));Latialatices
B
31

CopyOnWriteArrayList

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

Baun answered 21/7, 2015 at 8:7 Comment(2)
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 synchronizedListTuttifrutti
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 ConcurrentLinkedQueueSolberg
L
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.

Lilylilyan answered 15/3, 2010 at 0:33 Comment(1)
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.Latialatices
A
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.

Ariel answered 8/3, 2014 at 15:8 Comment(2)
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.Weisman
@masi Then you just synchronize on a final Object instead every time you access the Collection in any way.Sthenic
Y
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

Youngs answered 11/3, 2017 at 3:2 Comment(0)
C
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())); 
Copenhaver answered 11/6, 2018 at 5:0 Comment(0)
V
-1

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);
Verified answered 14/3, 2010 at 23:13 Comment(1)
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.Chromo

© 2022 - 2024 — McMap. All rights reserved.