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

Spiegazione dettagliata del codice di sincronizzazione thread della collezione Java

Implementazione dell'interfaccia List come array variabile. Implementa tutte le operazioni elencate come opzionali, e permette l'inclusione di elementi inclusi null. Oltre all'implementazione dell'interfaccia List, questa classe fornisce alcuni metodi per operare sulla dimensione dell'array interno utilizzato per memorizzare la lista. (Questa classe è simile alla classe Vector, eccetto che questa non è sincronizzata.) Le operazioni size, isEmpty, get, set, iterator e listIterator si eseguono in tempo costante. L'operazione add si esegue in tempo costante distribuito, il che significa che l'aggiunta di n elementi richiede un tempo O(n). Tutte le altre operazioni si eseguono in tempo lineare (parlando in generale). Il fattore costante di questa implementazione è inferiore rispetto a quello utilizzato per l'implementazione LinkedList. Ogni istanza di ArrayList ha una capacità. La capacità è la dimensione dell'array utilizzato per memorizzare gli elementi della lista. Questa è sempre almeno uguale alla dimensione della lista. Con l'aggiunta continua di elementi all'ArrayList, la capacità si espande automaticamente. I dettagli della strategia di crescita non sono specificati, perché non è semplice che l'aggiunta di elementi comporti un costo di tempo distribuito costante. Prima di aggiungere un gran numero di elementi, l'applicazione può utilizzare l'operazione ensureCapacity per aumentare la capacità dell'istanza di ArrayList. Questo può ridurre il numero di riallocazioni incrementali.

Attenzione, questa implementazione non è sincronizzata.

Se più thread accedono contemporaneamente a un'istanza di ArrayList e almeno uno di essi modifica la lista strutturalmente, allora deve mantenere la sincronizzazione esterna. Le modifiche strutturali includono qualsiasi operazione di aggiunta o rimozione di uno o più elementi, o esplicita regolazione della dimensione dell'array sottostante; l'impostazione del valore degli elementi non è una modifica strutturale. Questo viene generalmente completato attraverso operazioni di sincronizzazione sull'oggetto che naturalmente incapsula la lista. Se non esiste tale oggetto, la lista dovrebbe essere "incapsulata" utilizzando il metodo Collections.synchronizedList. Questo è meglio fare al momento della creazione per prevenire accessi inaspettati non sincronizzati alla lista:

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

Questi iterator e listIterator restituiti dalle metodi iterator e listIterator sono di rapido fallimento: dopo aver creato l'iteratore, a meno che non venga modificata la struttura della lista tramite il metodo remove o add dell'iteratore stesso, l'iteratore lancerà una ConcurrentModificationException in qualsiasi momento e in qualsiasi modo la lista venga modificata. Pertanto, di fronte a modifiche concorrenti, l'iteratore fallirà rapidamente, piuttosto che correre il rischio di comportamenti incerti in un momento futuro incerto.

Attenzione, il comportamento di fallimento rapido dell'iteratore non può essere garantito, perché in generale, non è possibile fare qualsiasi tipo di garanzia rigida riguardo alla possibilità di modifiche concorrenti non sincronizzate. Gli iteratori di fallimento rapido faranno del loro meglio per lanciare ConcurrentModificationException. Pertanto, scrivere un programma che dipende da questo eccezione per migliorare la correttezza di questi iteratori è un approccio sbagliato: il comportamento di fallimento rapido dell'iteratore dovrebbe essere utilizzato solo per rilevare bug.

Come mostrato di seguito, ora si crea un elenco di liste, un thread esegue operazioni di scrittura sul set, un altro thread esegue operazioni di eliminazione

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
public class MyArrayList {
	/** 
   * Creare un elenco, un thread per la scrittura e un thread per la lettura, l'iteratore e listIterator restituiti dai metodi iterator e listIterator sono rapidamente fallenti 
   */
	public void readWrite() {
		List<Integer> nums = new ArrayList<Integer>();
		List<Integer> synNums = Collections.synchronizedList(nums);
		// Avviare il thread di scrittura 
		new WriteListThread(synNums).start();
		// Avviare il thread di eliminazione 
		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;
	}
	// Scrivere ininterrottamente l'elemento 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;
	}
	// Eliminare l'elemento primo 
	public void run() {
		while (true) {
			try{
				System.out.println(Thread.currentThread().getName()+":"+nums.remove(0));
			}
			catch(Exception e){
				continue ;
			}
		}
	}
}

Attraverso List<Integer> synNums = Collections.synchronizedList(nums); si può eseguire la sincronizzazione delle operazioni atomiche, ma perché l'esempio dell'API ufficiale richiede di aggiungere manualmente la sincronizzazione?

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

Visualizzare il codice sorgente di 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 {
	/** 
   * Creare un elenco, un thread per la scrittura e un thread per la lettura, l'iteratore e listIterator restituiti dai metodi iterator e listIterator sono rapidamente fallenti 
   */
	public void readWrite() {
		List<Integer> nums = new ArrayList<Integer>();
		List<Integer> synNums = Collections.synchronizedList(nums);
		// Avviare il thread di scrittura 
		new WriteListThread(synNums).start();
		// Avviare il thread di eliminazione 
		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;
	}
	// Scrivere ininterrottamente l'elemento 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;
	}
	// Eliminare l'elemento primo 
	public void run() {
		while (true) {
			try{
				System.out.println(Thread.currentThread().getName()+":"+nums.remove(0));
			}
			catch(Exception e){
				continue ;
			}
		}
	}
}

Per le operazioni同步 sugli insiemi, oltre agli strumenti di condivisione sincronizzati di Collections, l'utente deve anche eseguire la sincronizzazione manualmente per le operazioni non atomiche

Come mostrato di seguito, aggiungere un thread per leggere l'insieme

class ReadListThread extends Thread {
	private List<Integer> nums;
	public ReadListThread(List<Integer> nums) {
		super(“ReadListThread”);}}
		this.nums = nums;
	}
	//Continuo a leggere elementi, operazione non atomica, quindi è necessario aggiungere manualmente il lock 
	public void run() {
		while (true) {
			//Riposo, consegna del lock a un altro thread 
			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();
					}
				}
			}
		}
	}
}

Sommario

Questo è tutto il contenuto dettagliato dell'articolo su come sincronizzare il codice della struttura di raccolta Java, speriamo che sia utile a tutti. Chi è interessato può continuare a consultare altre sezioni correlate di questo sito, e se ci sono punti deboli, siamo lieti di ricevere commenti. Grazie per il supporto dei nostri amici a questo sito!

Dichiarazione: il contenuto di questo articolo è stato raccolto da Internet, il copyright spetta ai rispettivi autori, il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, questo sito non detiene i diritti di proprietà, non è stato editato manualmente e non assume alcuna responsabilità legale correlata. Se trovi contenuti sospetti di violazione del copyright, ti preghiamo di inviare una e-mail a: notice#oldtoolbag.com (al momento dell'invio dell'e-mail, sostituisci # con @) per segnalare il problema e fornire prove pertinenti. Una volta verificata, questo sito rimuoverà immediatamente il contenuto sospetto di violazione del copyright.

Ti potrebbe interessare