14 Stimmen

wie man eine synchronisierte Array-Liste erstellt

Ich habe synchronisierte arrayList wie folgt erstellt

import java.text.SimpleDateFormat;
import java.util.*;

class HelloThread  
{

 int i=1;
 List arrayList;
  public  void go()
  {
 arrayList=Collections.synchronizedList(new ArrayList());
 Thread thread1=new Thread(new Runnable() {

  public void run() {
  while(i<=10)
  {
   arrayList.add(i);
   i++;
  }
  }
 });
 thread1.start();
 Thread thred2=new Thread(new Runnable() {
  public void run() {
     while(true)
     {
   Iterator it=arrayList.iterator();
      while(it.hasNext())
      {
       System.out.println(it.next());
      }
     }
  }
 });
 thred2.start();
  }
 }

public class test
{
  public static void main(String[] args)
  {
   HelloThread hello=new HelloThread();
   hello.go();
  }
}

aber ich erhalte eine Ausnahme wie diese

Ausnahme im Thread "Thread-1" java.util.ConcurrentModificationException

Stimmt etwas nicht mit meinem Ansatz?

28voto

axtavt Punkte 233070

Iterator von synchronizedList nicht synchronisiert ist (und nicht synchronisiert werden kann), müssen Sie die Liste während der Iteration manuell synchronisieren (siehe javadoc ) :

synchronized(arrayList) {
    Iterator it=arrayList.iterator(); 
    while(it.hasNext()) { 
        System.out.println(it.next()); 
   } 
}

Ein anderer Ansatz ist die Verwendung eines CopyOnWriteArrayList 代わりに Collections.synchronizedList() . Es implementiert eine Copy-on-Write-Semantik und erfordert daher keine Synchronisation.

12voto

dogbane Punkte 253146

Erwägen Sie die Verwendung eines CopyOnWriteArrayList die thread-sicher ist. Jedes Mal, wenn Sie ein Element hinzufügen, wird eine neue Kopie des zugrunde liegenden Arrays erstellt. Der Iterator spiegelt jedoch nicht die Hinzufügungen zur Liste seit der Erstellung des Iterators wider, aber es ist garantiert, dass er keine ConcurrentModificationException .

arrayList=new CopyOnWriteArrayList();

12voto

Stephen C Punkte 665668

Andere Antworten haben das Problem erkannt:

  • Die Iteratoren für synchronisierte Sammlungen sind nicht synchronisiert. Sie sind einfach die Iteratoren, die von den Sammlungsobjekten innerhalb der Wrapper-Klassen zurückgegeben werden.

  • Viele Sammlungsklassen (einschließlich ArrayList ) verwenden einen ausfallsicheren Mechanismus, um gleichzeitige Änderungen während der Iteration zu erkennen. Dieses Verhalten ist in den Javadocs für die jeweiligen Klassen eindeutig dokumentiert. Dies ist, was Sie sehen.

Dies ist nicht bei allen Sammelklassen der Fall. Zum Beispiel, viele der java.util.Concurrent... Sammlungsklassen erlauben gleichzeitige Änderungen während der Iteration, lockern aber die Semantik der Iterationsfolge, so dass die Ergebnisse der Änderungen kann, muss aber nicht in den vom Iterator zurückgegebenen Objekten ersichtlich sein.

Die Javadoc für die Collections.synchronizedList() erklärt, wie man den Iterator synchronisiert. Im Grunde tun Sie dies:

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

(Nebenbei bemerkt: Normalerweise ist es nicht sicher anzunehmen, dass so etwas funktioniert. Theoretisch könnte die synchronisierte Liste ein privates Sperrobjekt verwenden, und die synchronized Anweisung würde gleichzeitige Änderungen nicht ausschließen. Die Javadocs sagen jedoch, dass dies in diesem Fall zu tun ist ... es ist also sicher).

Das Problem dabei ist, dass durch das Sperren der Sammlung ein potenzieller Engpass für die Gleichzeitigkeit entsteht. Die Alternative dazu ist die Verwendung einer Copy-on-Write-Datenstruktur, die intern eine Kopie der relevanten Teile der Sammlung erstellt. Dieser Ansatz bedeutet, dass ein Iterator eine Momentaufnahme der Sammlung sieht. Änderungen an der Sammlung können während einer Iteration vorgenommen werden, aber der Iterator sieht sie nicht. Das Problem bei Copy-on-Write ist, dass Änderungen potenziell sehr viel teurer sind.

Letztendlich müssen Sie die Merkmale und Kosten der verschiedenen Erfassungsarten im Hinblick auf die gleichzeitige Änderung gegenüber Ihren tatsächlichen Anforderungen abwägen. Können Sie sich damit abfinden, dass der Iterator nicht alle gleichzeitigen Änderungen sieht?

3voto

stacker Punkte 65961

Die java.util.ConcurrentModificationException tritt auf, wenn Sie eine Sammlung manipulieren (hinzufügen, entfernen), während Sie über dieselbe Sammlung iterieren.

Wahrscheinlich möchten Sie die erstellten Einträge in Ihrem zweiten Thread verbrauchen, nachdem sie von Ihrem ersten Thread erstellt wurden. Sie könnten also ArrayLists verwenden get( index ) und Größe () zur Kontrolle

2voto

Bart van Heukelom Punkte 41768

Wie Spike schon sagte, kann man eine Sammlung nicht ändern, während sie iteriert wird. Ich denke jedoch, die Lösung ist, die Liste während der Iteration zu sperren.

class HelloThread  
{

 int i=1;
 List arrayList;
  public  void go()
  {
 arrayList=Collections.synchronizedList(new ArrayList());
 Thread thread1=new Thread(new Runnable() {

  public void run() {
  while(i<=10)
  {
synchronized(someLock) {
   arrayList.add(i);
}
   i++;
  }
  }
 });
 thread1.start();
 Thread thred2=new Thread(new Runnable() {
  public void run() {
     while(true)
     {
synchronized(someLock) {
   Iterator it=arrayList.iterator();
      while(it.hasNext())
      {
       System.out.println(it.next());
      }
}
     }
  }
 });
 thred2.start();
  }
 }

public class test
{
  public static void main(String[] args)
  {
   HelloThread hello=new HelloThread();
   hello.go();
  }
}

Ich bin mir nicht sicher, was Sie vorhaben, und hoffe, dass dies die Funktionalität Ihres Codes nicht beeinträchtigt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X