English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Подробное объяснение кода синхронизации потоков в java-коллекциях

Реализация List интерфейса для изменяемого массива. Реализует все опциональные операции списка и позволяет включать все элементы, включая null. Помимо реализации List интерфейса, этот класс предоставляет некоторые методы для управления размером массива, используемого для хранения списка. (Этот класс大致 эквивалент Vector классу, за исключением того, что этот класс не синхронизирован.) Операции size, isEmpty, get, set, iterator и listIterator выполняются с фиксированным временем. Операция add выполняется с摊уваемым фиксированным временем, то есть добавление n элементов требует времени O(n). Все другие операции выполняются с линейным временем (в общем смысле). По сравнению с константным коэффициентом LinkedList реализации, этот коэффициент更低. У каждого экземпляра ArrayList есть емкость. Эмкость означает размер массива, используемого для хранения элементов списка. Она всегда по крайней мере равна размеру списка. С увеличением элементов, добавляемых в ArrayList, его емкость также автоматически увеличивается. Подробности стратегии роста не указаны, так как это не просто добавление элементов, что приводит к摊уваемым фиксированным затратам времени. Перед добавлением большого количества элементов приложение может использовать операцию ensureCapacity для увеличения емкости экземпляра ArrayList. Это может уменьшить количество увеличений перераспределения.

Обратите внимание, что это реализация не синхронизирована.

Если несколько потоков одновременно доступа к одному экземпляру ArrayList, и по крайней мере один поток изменяет структуру списка, то он должен поддерживать внешнюю синхронизацию. Структурированные изменения включают в себя любые операции по добавлению или удалению одного или нескольких элементов, или явное изменение размера базового массива; изменение значения элемента не является структурным изменением. Обычно это достигается через синхронизацию объекта, естественно обертывающего список. Если такого объекта нет, то список должен быть 'упакован' с помощью метода Collections.synchronizedList. Это лучше сделать при создании, чтобы предотвратить случайный несинхронный доступ к списку:

List list = Collections.synchronizedList(new ArrayList(...));

Такие итераторы, возвращаемые методами iterator и listIterator, являются быстрый失败的: после создания итератора, если структура списка изменяется любым способом, кроме использования методов remove или add итератора самого, итератор всегда выбрасывает ConcurrentModificationException. Таким образом, в случае параллельных изменений итератор быстро полностью失败, а не рискует в будущем произвольным поведением в неопределенное время.

Обратите внимание, что поведение быстрого отказа итератора не может быть гарантировано, потому что, как правило, невозможно сделать какие-либо жесткие гарантии по поводу возникновения несинхронизированных параллельных изменений. Быстрый отказ итератора будет максимально стараться выбрасывать ConcurrentModificationException. Поэтому программирование, которое зависит от этого исключения для повышения точности таких итераторов, является неправильным: поведение быстрого отказа итератора должно использоваться только для детектирования ошибок.}

Как показано выше, сейчас создается список, один поток выполняет写入 в список, а другой поток выполняет удаление из списка

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
public class MyArrayList {
	/** 
   * Создать список, один поток выполняет写入, другой поток читает iterator и listIterator методы возвращают быстрый失败的 итератор 
   */
	public void readWrite() {
		List<Integer> nums = new ArrayList<Integer>();
		List<Integer> synNums = Collections.synchronizedList(nums);
		// Запустить поток записи 
		new WriteListThread(synNums).start();
		// Запустить удаление потока 
		new DeleteListThread(synNums).start();
	}
	public static void main(String[] args) {
		new MyArrayList().readWrite();
	}
}
class WriteListThread extends Thread {
	private List<Integer> nums;
	public WriteListThread(List<Integer> nums) {
		super(“WriteListThread”);
		this.nums = nums;
	}
	// Непрерывно записывать элемент 1 
	public void run() {
		while (true) {
			nums.add(new Random().nextint(1000));
			System.out.println(Thread.currentThread().getName());
		}
	}
}
class DeleteListThread extends Thread {
	private List<Integer> nums;
	public DeleteListThread(List<Integer> nums) {
		super(“DeleteListThread”);
		this.nums = nums;
	}
	// Удалить первый элемент 
	public void run() {
		while (true) {
			try{
				System.out.println(Thread.currentThread().getName()+":"+nums.remove(0));
			}
			catch(Exception e){
				continue ;
			}
		}
	}
}

Через List<Integer>synNums=Collections.synchronizedList(nums); можно выполнить синхронизацию атомарных операций, но почему официальные примеры API требуют ручной синхронизации?

List list = Collections.synchronizedList(new ArrayList()); 
 synchronized(list) { 
   Iterator i = list.iterator(); // Must be in synchronized block 
   while (i.hasNext()) 
     foo(i.next()); 
 } 

Просмотрите исходный код Collections.synchronizedList

SynchronizedCollection(Collection<E> c) { 
      if (c==null) 
        throw new NullPointerException(); 
    this.c = c; 
      mutex = this; 
    } 
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
public class MyArrayList {
	/** 
   * Создать список, один поток выполняет写入, другой поток читает iterator и listIterator методы возвращают быстрый失败的 итератор 
   */
	public void readWrite() {
		List<Integer> nums = new ArrayList<Integer>();
		List<Integer> synNums = Collections.synchronizedList(nums);
		// Запустить поток записи 
		new WriteListThread(synNums).start();
		// Запустить удаление потока 
		new DeleteListThread(synNums).start();
	}
	public static void main(String[] args) {
		new MyArrayList().readWrite();
	}
}
class WriteListThread extends Thread {
	private List<Integer> nums;
	public WriteListThread(List<Integer> nums) {
		super("WriteListThread");
		this.nums = nums;
	}
	// Непрерывно записывать элемент 1 
	public void run() {
		while (true) {
			nums.add(new Random().nextint(1000));
			System.out.println(Thread.currentThread().getName());
		}
	}
}
class DeleteListThread extends Thread {
	private List<Integer> nums;
	public DeleteListThread(List<Integer> nums) {
		super("DeleteListThread");
		this.nums = nums;
	}
	// Удалить первый элемент 
	public void run() {
		while (true) {
			try{
				System.out.println(Thread.currentThread().getName()+":"+nums.remove(0));
			}
			catch(Exception e){
				continue ;
			}
		}
	}
}

可见对于集合的同步操作,除了使用Collections的同步包装工具类外,用户还需要手动进行同步非原子操作

Как показано ниже, добавьте поток для чтения集合

class ReadListThread extends Thread {
	private List<Integer> nums;
	public ReadListThread(List<Integer> nums) {
		super(“ReadListThread”);
		this.nums = nums;
	}
	//Continuous reading of elements, non-atomic operation, manual locking is required 
	public void run() {
		while (true) {
			//Sleep, give the lock to other threads 
			try {
				Thread.sleep(1000);
			}
			catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			synchronized (nums) {
				if (nums.size() > 100) {
					Iterator<Integer> iter = nums.iterator();
					while (iter.hasNext()) {
						System.out.println(Thread.currentThread().getName()) 
						                + "":" + iter.next());
						;
					}
				} else{
					try {
						nums.wait(1000);
					}
					catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

Обобщение

Вот полный контент статьи о деталях синхронизации кода коллекции Java, надеюсь, он поможет вам. Те, кто интересуется, могут продолжить читать другие связанные темы на этом сайте, и если есть недостатки, пожалуйста, оставляйте комментарии. Спасибо за поддержку сайта!

Заявление: контент этой статьи был взят из Интернета, авторские права принадлежат соответствующему автору. Контент был предложен пользователями Интернета и загружен самостоятельно. Этот сайт не обладает правами собственности на него, не производил редактирование вручную и не несет ответственности за соответствующие юридические последствия. Если вы обнаружите спорный контент, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (во время отправки письма замените # на @) для жалоб и предоставьте соответствующие доказательства. При подтверждении факта нарушения авторских прав сайт незамедлительно удаляет спорный контент.

Основной учебник
Рекомендуем