Кратко поговорим о CopyOnWriteArrayList в Java.

Java задняя часть Безопасность
Кратко поговорим о CopyOnWriteArrayList в Java.

1,Copy-On-Writeчто это?

Сначала позвольте мне рассказать вам, что такоеCopy-On-Write, как следует из названия, в компьютере, когда вы хотите модифицировать кусок памяти, мы не делаем это в исходном блоке памятиоперацию, но скопируйте копию памяти и выполните ее в новой памятидействовать,После этого наведите указатель на исходную память на новую память, и исходную память можно переработать!

Братья в Интернете сказали, что это своего рода программирование, используемое в программировании优化策略, это延时懒惰策略. Говорят, что оптимизация оптимизируется, так какие проблемы оптимизируются?

Сначала дайте вам код:

public class IteratorTest {

	private static List<String> list = new ArrayList<>();

	public static void main(String[] args) {
		
		list.add("1");
		list.add("2");
		list.add("3");
		
		Iterator<String> iter = list.iterator();
		
		//我当前正在迭代集合(这里模拟并发中读取某一list的场景)
		while (iter.hasNext()) {
			
			System.err.println(iter.next());
		
		}
		
		System.err.println(Arrays.toString(list.toArray()));
	}
}

Приведенный выше фрагмент программы хорош при выполнении в одном потоке, но в многопоточном окружении это может быть ГГ! Зачем? Поскольку в многопоточной среде другим потокам не разрешено добавлять элементы в этот список коллекций при итерации.Посмотрите на следующий код, вы обнаружите, что выбросjava.util.ConcurrentModificationExceptionисключение.

public class IteratorTest {

	private static List<String> list = new ArrayList<>();

	public static void main(String[] args) {

		list.add("1");
		list.add("2");
		list.add("3");

		Iterator<String> iter = list.iterator();

		// 存放10个线程的线程池
		ExecutorService service = Executors.newFixedThreadPool(10);

		// 执行10个任务(我当前正在迭代集合(这里模拟并发中读取某一list的场景))
		for (int i = 0; i < 10; i++) {
			service.execute(new Runnable() {
				@Override
				public void run() {
					while (iter.hasNext()) {
						System.err.println(iter.next());
					}
				}
			});
		}
		
		// 执行10个任务
		for (int i = 0; i < 10; i++) {
			service.execute(new Runnable() {
				@Override
				public void run() {
					list.add("121");// 添加数据
				}
			});
		}
		
		System.err.println(Arrays.toString(list.toArray()));
		
	}
}
  • 1. здесь迭代Указывает, что в настоящее время я читаю некоторые集合данные вдействовать;
  • 2. Поток имитирует, что текущая программа находится в многопоточной среде, а другие потоки изменяют данные.

Какая проблема раскрыта здесь?

  • 1. Многопоточность повлияет на итеративный сбор и повлияет на операцию чтения

решать:

  • 1,CopyOnWriteArrayListИзбегайте проблемы многопоточной работы Небезопасность списка потоков

2,CopyOnWriteArrayListвводить

Начиная с JDK1.5, в параллельном пакете Java предусмотрено два варианта использования.CopyOnWriteмеханизм для реализации параллельных контейнеров, ониCopyOnWriteArrayListиCopyOnWriteArraySet.CopyOnWriteКонтейнеры очень полезны и могут использоваться во многих сценариях параллелизма.

CopyOnWriteArrayListпринцип:

上面已经讲了,就是在写的时候不对原集合进行修改,而是重新复制一份,修改完之后,再移动指针

Так вы можете спросить? Даже если исходная коллекция будет скопирована, не вызовет ли это конфликтов записи в многопоточной среде? Да, но вы можете этого еще не знатьCopyOnWriteArrayListДобавьте детали реализации удаления элементов вadd()方法

3.CopyOnWriteArrayListПростая интерпретация исходного кода

add()Исходный код метода:

/**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    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);//将引用指向新数组  1
            return true;
        } finally {
            lock.unlock();//解锁啦
        }
    }

Внезапно понял, проба, оказываетсяadd()При добавлении коллекции добавляется блокировка, чтобы обеспечить синхронизацию и избежать копирования N копий при многопоточной записи. (想想,你在遍历一个10个元素的集合,每遍历一次有1人调用add方法,你说当你遍历10次,这add方法是不是得被调用10次呢?是不是得copy出10分新集合呢?万一这个集合非常大呢?)

Так? вы все еще спрашиваете?CopyOnWriteArrayListКак решить проблему потокобезопасности? Ответ-写时复制,加锁Еще спрашивать? Так бывает ли такая ситуация, когда поток только что закончил вызыватьadd()метод, который только что выполняется для вышеуказанного1Код в , то есть просто указать ссылку на массив сердца, и есть ли поток, обходящий его в это время? Сообщит об ошибке? (答案是不会的,因为你正在遍历的集合是旧的,这就有点难受啦,哈哈~)

Когда вы помещаете приведенный выше кодArrayListизменить наCopyOnWriteArrayList, выполнение не сообщит об ошибке!

public class IteratorTest {

	private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

	public static void main(String[] args) {

		list.add("1");
		list.add("2");
		list.add("3");

		Iterator<String> iter = list.iterator();

		// 存放10个线程的线程池
		ExecutorService service = Executors.newFixedThreadPool(10);

		// 执行10个任务(我当前正在迭代集合(这里模拟并发中读取某一list的场景))
		for (int i = 0; i < 10; i++) {
			service.execute(new Runnable() {
				@Override
				public void run() {
					while (iter.hasNext()) {
						System.err.println(iter.next());
					}
				}
			});
			service.execute(new Runnable() {
				@Override
				public void run() {
					list.add("121");// 添加数据
				}
			});
		}
		
		// 执行10个任务
		for (int i = 0; i < 10; i++) {
			service.execute(new Runnable() {
				@Override
				public void run() {
					list.add("121");// 添加数据
				}
			});
			service.execute(new Runnable() {
				@Override
				public void run() {
					while (iter.hasNext()) {
						System.err.println(iter.next());
					}
				}
			});
		}
		
		System.err.println(Arrays.toString(list.toArray()));
		
	}
}

4.CopyOnWriteArrayListПреимущества и недостатки

недостаток:

  • 1. Потребление памяти (репликация коллекции)
  • 2. Производительность в реальном времени невысокая

преимущество:

  • 1. Согласованность данных полная, почему? Из-за блокировки параллельные данные не будут хаотичными
  • 2, решено像ArrayList,VectorТакого рода задача многопоточной итерации обхода коллекции, помните,VectorХотя потокобезопасный, он просто добавляетsynchronizedКлючевое слово, итерационная задача вообще не решена!

5.CopyOnWriteArrayListсцены, которые будут использоваться

  • 1. Больше читать и меньше писать (белый список, черный список, сценарии доступа и обновления товарных категорий), почему? Потому что новая коллекция копируется при записи
  • 2. Коллекция не большая, почему? Потому что новая коллекция копируется при записи
  • Требования к реальному времени не высоки, почему, ведь есть возможность читать старые данные коллекции

Справочная статья:Как безопасно перемещаться по списку: Vector, CopyOnWriteArrayList


Редактор старший, стажер, и его знания все еще поверхностны Добро пожаловать, чтобы комментировать и обмениваться, учиться вместе и добиваться прогресса вместе!