user2159457
user2159457

Reputation: 15

Multiple threads editing an ArrayList and its objects

I am trying to run 2 concurrent threads, where one keeps adding objects to a list and the other updates these objects and may remove some of these objects from the list as well. I have a whole project where I've used ArrayList for my methods and classes so it's difficult to change it now.

I've looked around and I found a few ways of doing this, but as I said it is difficult to change from ArrayList. I tried using synchronized and notify() for the method adding the objects to the list and wait() for the method changing these objects and potentially removing them if they meet certain criteria.

Now, I've figured out how to do this using a CopyOnWriteArrayList, but I would like to know if there's a possibility of using ArrayList itself to simulate this. so that I don't have to edit my entire code.

So, basically, I would like to do something like this, but with ArrayList:

import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;

public class ListExample{
    CopyOnWriteArrayList<MyObject> syncList;

    public ListExample(){
        syncList = new CopyOnWriteArrayList<MyObject>();

        Thread thread1 = new Thread(){
            public void run(){
                synchronized (syncList){
                    for(int i = 0; i < 10; i++){
                        syncList.add(new MyObject(i));
                    }
                }
            }
        };

        Thread thread2 = new Thread(){
            public void run(){
                synchronized (syncList){
                    Iterator<MyObject> iterator = syncList.iterator();
                    while(iterator.hasNext()){
                        MyObject temp = iterator.next();

                        //this is just a sample list manipulation
                        if (temp.getID() > 3)
                            syncList.remove(temp);

                        System.out.println("Object ID: " + temp.getID() + " AND list size: " + syncList.size());
                    }
                }
            }
        };

        thread1.start();
        thread2.start();
    }

    public static void main(String[] args){
        new ListExample();
    }
}

class MyObject{
    private int ID;

    public MyObject(int ID){
        this.ID = ID;
    }

    public int getID(){
        return ID;
    }

    public void setID(int ID){
        this.ID = ID;
    }
}

I've also read about Collections.synchronizedList(new ArrayList()) but again, I believe this would require me to change my code as I have a substantial number of methods that take ArrayList as a parameter.

Any guidance would be appreciated, because I am out of ideas. Thank you.

Upvotes: 1

Views: 6552

Answers (4)

vishal_aim
vishal_aim

Reputation: 7854

Of course you should be using java.util.concurrent pakage. But let's look at what is happening/could happen with only ArrayList and synchronization.

In your code, if you have just ArrayList in place of CopyOnWriteArrayList, it should work as you have provided full synchronization synchronized (syncList) on whatever you are doing/manipulating in threads. You do not require any wait() notify() if whole thing is synchronized (But that's not recommended, will come to that).

But this code will give ConcurrentModificationException because once you are using iterator syncList.iterator() you should not remove element from that list, otherwise it may give undesirable results while iterating that's why it's designed to fail fast and give exception. To avoid this you can use like:

                Iterator<MyObject> iterator = syncList.iterator();
                ArrayList<MyObject> toBeRemoved = new ArrayList<MyObject>();
                while(iterator.hasNext()){
                    MyObject temp = iterator.next();

                    //this is just a sample list manipulation
                    if (temp.getID() > 3)
                    {
                        //syncList.remove(temp);
                        toBeRemoved.add(temp);
                    }
                    System.out.println("Object ID: " + temp.getID() + " AND list size: " + syncList.size());
                }
                syncList.removeAll(toBeRemoved);

Now regarding synchronization, you should strive to minimize its scope otherwise there'll be unnecessary waiting between threads, thats why java.util.concurrent package is given to have high performance in multithreading (using even non blocking algorithms). Or you can also use Collections.synchronizedList(new ArrayList()) but they are not as good as concurrent classes.

If you want to use conditional synchronization like in producer/consumer problem, then you can use wait() notify() mechanism on same object (lock). But again there're already some classes to help like using java.util.concurrent.LinkedBlockingQueue.

Upvotes: 1

gparyani
gparyani

Reputation: 1988

Use Vector instead of ArrayList. Remember to store it in a List reference as Vector contains deprecated methods. Vector, unlike ArrayList, synchronizes its internal operations, and unlike CopyOnWriteArrayList, does not copy the internal array each time a modification is made.

Upvotes: 1

Ankit
Ankit

Reputation: 6622

As a quick solution u may extend ArrayList and make modifying methods (add/remove) synchronized. and re-factor code to replace ArrayList to your custom-ArrayList

Upvotes: 1

Javier
Javier

Reputation: 12398

You may be interested on the collections provided by the java.util.concurrent package. They are very useful for producer/consumer scenarios, where one or more threads add things to a queue, and other threads take them. There are different methods depending on whether you want to block, or fail when the queue is full/empty.

About refactoring your methods, you should have used interfaces (e.g. List) instead of concrete implementation classes (such as ArrayList). That is the purpose of interfaces, and the Java API has a good suply of them.

Upvotes: 5

Related Questions