#java #multithreading #reentrantlock
#java #многопоточность #повторная блокировка
Вопрос:
Зачем нам нужно получать 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:
Когда вы пытаетесь выполнить какую-либо операцию с глобальной переменной в многопоточном контексте и хотите, чтобы она была как атомарной, так и обеспечивала видимость памяти для других потоков, вам необходимо иметь блокировку вокруг этой операции.
Здесь 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
и присваивают новый элемент одному и тому же индексу.
Итак, какой бы поток ни назначил новое значение в конце, он перезапишет значение, установленное ранее другим потоком.
Чтобы объяснить дальше, скажем, что и поток один, и поток два считывают одно и то же значение len
теперь поток один продолжает создавать новый массив Arrays.copyOf(elements, len 1)
и присваивает значение переменной e
в len
позиции нового массива.
И перед потоком можно установить новый массив, используя setArray(newElements)
второй поток, тем временем продолжая этот процесс с тем же значением len
. Хотя это создаст новый экземпляр массива, но индекс, в котором установлен новый элемент, будет таким же, как len
используемый первым потоком.
Таким образом, когда поток two использует setArray(newElements)
для установки нового массива с новым значением после потока one, более раннее значение массива в индексе len
th будет перезаписано новым элементом, установленным потоком two.