Получение блокировок для операции добавления в CopyOnWriteArrayList - PullRequest
3 голосов
/ 13 апреля 2019

Почему нам нужно получить блокировку Reentrant согласно приведенному ниже коду в CopyOnWriteArrayList, когда мы добавляем элементы в List. Мы создаем копию исходного массива, а затем модифицируем его. Какие побочные эффекты мы можем иметь, если не получим lock?

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

1 Ответ

2 голосов
/ 13 апреля 2019

Когда вы пытаетесь выполнить какую-либо операцию с глобальной переменной в многопоточном контексте и хотите, чтобы она была атомарной и обеспечивала видимость памяти для других потоков, вам нужно иметь блокировку вокруг этой операции.

Здесь getArray() возвращает глобальное поле экземпляра Object[] array.

Итак, в этом примере:

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

Если вокруг этого блока кода нет блокировки и предполагается, что два потока пытаются добавить элемент, в этом случае может случиться, что один поток и второй поток оба прочитают одинаковое значение len и назначьте новый элемент тому же индексу.

Так, какой бы поток ни назначал новое значение в конце, он будет перезаписывать значение, ранее установленное другим потоком.

Чтобы объяснить далее, скажем, что поток 1 и поток 2 читают одно и то же значение len, теперь поток 1 продолжает создавать новый массив из Arrays.copyOf(elements, len + 1) и присваивает значение переменной e в * 1024. * позиция нового массива.

И до того, как поток может установить новый массив, используя setArray(newElements), поток два тем временем продолжает этот процесс с тем же значением len. Хотя это создаст новый экземпляр массива, но индекс, в котором установлен новый элемент, будет таким же, как len, используемый потоком один.

Таким образом, когда второй поток использует setArray(newElements) для установки нового массива с новым значением после первого потока, более раннее значение массива в len th index будет перезаписано новым набором элементов по нитке два.

...