Докажите, что CopyOnWriteArrayList является потокобезопасным.
Напишите кусок кода, чтобы доказатьCopyOnWriteArrayList
Это действительно потокобезопасно.
ReadThread.java
import java.util.List;
public class ReadThread implements Runnable {
private List<Integer> list;
public ReadThread(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
for (Integer ele : list) {
System.out.println("ReadThread:"+ele);
}
}
}
WriteThread.java
import java.util.List;
public class WriteThread implements Runnable {
private List<Integer> list;
public WriteThread(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
this.list.add(9);
}
}
TestCopyOnWriteArrayList.java
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestCopyOnWriteArrayList {
private void test() {
//1、初始化CopyOnWriteArrayList
List<Integer> tempList = Arrays.asList(new Integer [] {1,2});
CopyOnWriteArrayList<Integer> copyList = new CopyOnWriteArrayList<>(tempList);
//2、模拟多线程对list进行读和写
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new ReadThread(copyList));
executorService.execute(new WriteThread(copyList));
executorService.execute(new WriteThread(copyList));
executorService.execute(new WriteThread(copyList));
executorService.execute(new ReadThread(copyList));
executorService.execute(new WriteThread(copyList));
executorService.execute(new ReadThread(copyList));
executorService.execute(new WriteThread(copyList));
System.out.println("copyList size:"+copyList.size());
}
public static void main(String[] args) {
new TestCopyOnWriteArrayList().test();
}
}
Выполнение приведенного выше кода не сообщает
java.util.ConcurrentModificationException
Это показывает, что CopyOnWriteArrayList все еще может хорошо работать в параллельной многопоточной среде.
Как CopyOnWriteArrayList является потокобезопасным
CopyOnWriteArrayList
с помощью метода, называемогокопирование при записиметод, когда новый элемент добавляется вCopyOnWriteArrayList
Когда , сначала скопируйте копию из исходного массива, затем выполните операцию записи в новый массив и после записи укажите исходную ссылку массива на новый массив.
Когда добавляется новый элемент, как показано на рисунке ниже, создается новый массив, и в новый массив добавляется новый элемент.В это время ссылка массива по-прежнему указывает на исходный массив.
Когда элемент успешно добавлен в новый массив, ссылка на эту ссылку указывает на новый массив.
CopyOnWriteArrayList
Вся операция добавления находится вЗамокпод защитой.
Это сделано для того, чтобы избежать многопоточного параллельного добавления,сделать несколько копий, искажая данные, в результате чего окончательные данные массива не соответствуют нашим ожиданиям.
CopyOnWriteArrayList
изadd
Исходный код операции выглядит следующим образом:
public boolean add(E e) {
//1、先加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//2、拷贝数组
Object[] newElements = Arrays.copyOf(elements, len + 1);
//3、将元素加入到新数组中
newElements[len] = e;
//4、将array引用指向到新数组
setArray(newElements);
return true;
} finally {
//5、解锁
lock.unlock();
}
}
Поскольку все операции записи выполняются в новый массив, в это время, если есть параллельные записи потоками, они контролируются блокировками, а если есть параллельные чтения потоками, то возможны несколько ситуаций:
1. Если операция записи не завершена, прочитать данные исходного массива напрямую;
2. Если операция записи завершена, но ссылка еще не указала на новый массив, то также считываются данные исходного массива;
3. Если операция записи завершена и ссылка указывает на новый массив, то читаем данные непосредственно из нового массива.
видимый,CopyOnWriteArrayList
изоперация чтенияДа, ты можешьзамокиз.
Сценарии использования CopyOnWriteArrayList
Благодаря приведенному выше анализу,CopyOnWriteArrayList
Есть несколько недостатков:
1. Из-за необходимости копировать массив во время операции записи он будет потреблять память.Если содержимое исходного массива относительно велико, это может привести кyoung gc
илиfull gc
2, не может быть использован дляЧитать в режиме реального времениВ таких сценариях, как копирование массива и добавление новых элементов, требуется время, поэтому вызовитеset
После операции считанные данные могут быть все еще старыми, хотяCopyOnWriteArrayList
может сделать этовозможная согласованность, но все еще не может удовлетворить требования в реальном времени;
CopyOnWriteArrayList
подходящееБольше читайте и меньше пишитесцена, но используйте этот тип с осторожностью
Потому что никто не может гарантироватьCopyOnWriteArrayList
Сколько данных класть в итоге, если данных чуть больше, то массив нужно перекопировать каждый раз при добавлении/установке, это слишком высокая стоимость. В высокопроизводительных интернет-приложениях эта операция может привести к сбою за считанные минуты.
Идеи, раскрытые CopyOnWriteArrayList
Некоторые идеи, выраженные CopyOnWriteArrayList в приведенном выше анализе:
1. Разделение чтения и письма, разделение чтения и письма
2. Конечная согласованность
3. Используйте идею открытия пространства для решения конфликтов параллелизма
Оригинальная ссылка