0

From the CopyOnWriteArrayList.java, the add method is as follows:

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
    }

It's not hard to understand that add operation should lock, what confuses me is that it copy old data to new array and abandon the previous one. meanwhile get method is as follows:

public E get(int index) {
        return (E)(getArray()[index]);
    }

With no lock in get method. I find some explanations, some say copy to a new array can avoid add and get method operate on the same array. My problem is why two thread cannot read and write at the same time?

Bartosz Bilicki
  • 12,599
  • 13
  • 71
  • 113
bob zhou
  • 103
  • 1
  • 7
  • 3
    http://stackoverflow.com/questions/17853112/in-what-situations-is-the-copyonwritearraylist-suitable/17853225#17853225 – Ankur Lathi Sep 24 '13 at 06:00

3 Answers3

2

If you just look at the top of the class CopyOnWriteArrayList about array referance variablle declaration there is the answer of your question.

 private volatile transient Object[] array; // this is volatile

return (E)(getArray()[index]);

which returns latest copy of array[index] so this is threadsafe

final Object[] getArray() {
      return array;
  }

getArray is returning reference to array.

amicngh
  • 7,831
  • 3
  • 35
  • 54
  • I think I understand, thx. Another thing, if I defined volatile int i = 1; thread A read i, thread B i++; what kind of wrong result could thread A happen? – bob zhou Sep 24 '13 at 06:27
  • If you have marked variable i as `volatile` then there won't be any wrong result as volatile ensures the latest copy of i – amicngh Sep 24 '13 at 07:08
  • 1
    Note that marking a reference volatile does not make operations on it atomic. With `volatile int i = 1;`, `i++` is still a non-atomic *read-modify-write* operation, and performing it from multiple threads can result in lost updates. – bowmore Sep 24 '13 at 08:19
  • Please refer JLS http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4 – amicngh Sep 24 '13 at 08:44
1

Actually the reason that the write path locks is not because it needs to provide thread safety considering the read path, but because it wants to serialize writers. Since the copy-on-write technique replaces the volatile reference, it's usually best to serialize that operation.

The key to this idea is that writes are accomplished by copying the existing value, modifying it, and replacing the reference. It also follows that once set the object pointed by the reference is always read only (i.e. no mutation is done directly on the object referred by the reference). Therefore, readers can access it safely without synchronization.

Reads and writes can happen concurrently. However, the implication is that the reads will see the soon-to-be-stale state until the volatile reference set is done.

sjlee
  • 7,726
  • 2
  • 29
  • 37
0

At the time of get() if multiple threads try to get from the list their will be no issue. Because due to volatile array it will always read latest copy and return the element from array.

But

During add() or set() every time they created a new array to avoid mutual execution problems, this is one way to make objects thread safe to make the immutable.

If they have used same array object during add or set then they have to make traversal synchronized.or it may throw exception if any thread add/remove object to list during traversal

As per java doc

A thread-safe variant of java.util.ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array.

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

See this

package com.concurrent;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteArrayListTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        CopyOnWriteArrayList<Integer> list=new CopyOnWriteArrayList<>();


        Viewer viewer=new Viewer();
        viewer.setList(list);       
        Thread t1=new Thread(viewer);

        Adder adder=new Adder();
        adder.setList(list);

        Thread t=new Thread(adder);
        t.start();
        t1.start();

    }

    static class Adder implements Runnable{

        private List<Integer> list;
        public void setList(List<Integer> list) {
            this.list = list;
        }
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                list.add(i);
                System.out.println("Added-"+i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }

    }

    static class Viewer implements Runnable{

        private List<Integer> list;
        public void setList(List<Integer> list) {
            this.list = list;
        }
        @Override
        public void run() {
            while (true) {
                System.out.println("Length of list->"+list.size());
                for (Integer i : list) {
                    System.out.println("Reading-"+i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }

        }

    }
}
Ankit Katiyar
  • 2,631
  • 2
  • 20
  • 30