Search code examples
javamultithreadingreentrantlock

Acquiring locks for add operation in CopyOnWriteArrayList


Why do we need to acquire a Reentrant lock as per the below code in the CopyOnWriteArrayList when we add elements in the List. We are creating a copy of the original array and then modifying it. What side effects can we have if we don't acquire the lock in the first place?

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

Solution

  • When you are trying to do any operation on a global variable in a multi-threaded context and want it to be both atomic and ensure memory visibility to other threads you need to have a lock around that operation.

    Here getArray() is returning a global instance field Object[] array.

    So in this example:

    Object[] elements = getArray();
    int len = elements.length;
    Object[] newElements = Arrays.copyOf(elements, len + 1);
    newElements[len] = e;
    setArray(newElements);
    return true;
    

    If there is no lock around this code block and suppose two threads are trying to add an element, in that case it may happen that thread one and thread two both read the same value of the len and assign the new element to the same index.

    So which ever thread assigns the new value at the end will overwrite the value set earlier by the other thread.

    To explain further, say both thread one and thread two read the same value of len now thread one goes on to create the new array from Arrays.copyOf(elements, len + 1) and assigns the value of the variable e in the len position of the new array.

    And before the thread one can set the new array using setArray(newElements) thread two meanwhile continues this process with the same value of len. Although it will create a new array instance but the index at which the new element is set will be the same as len used by thread one.

    So the when the thread two uses setArray(newElements) to set the new array with the new value after thread one, the earlier array value at lenth index will be overwritten with the new element set by thread two.