2010-04-26 10 views

Antwort

42

Es gibt kein garantiertes Verhalten für das, was passiert, wenn Add gleichzeitig von zwei Threads auf ArrayList aufgerufen wird. Es ist jedoch meine Erfahrung, dass beide Objekte gut hinzugefügt wurden. Die meisten Thread-Sicherheitsprobleme, die sich auf Listen beziehen, behandeln die Iteration beim Hinzufügen/Entfernen. Trotzdem empfehle ich dringend die Verwendung von vanilla ArrayList mit mehreren Threads und gleichzeitigem Zugriff.

Vektor war der Standard für gleichzeitige Listen, aber jetzt ist der Standard die Collections synchronized list zu verwenden.

Ich empfehle auch Java Concurrency in Practice von Goetz et al, wenn Sie Zeit mit Threads in Java arbeiten werden. Das Buch behandelt dieses Problem viel detaillierter.

+13

"Es ist meine Erfahrung, dass beide Objekte gut hinzugefügt wurden" möchte nur darauf hinweisen, dass dies nur reines Glück ist. Wahrscheinlich ist das Fenster für ein Datenbeschädigungsproblem mit ArrayList extrem klein, aber es existiert immer noch –

+5

Richtig, und das ist mein Punkt. In den allermeisten Fällen wird es kein Problem geben. aber wir programmieren nicht auf die meisten Fälle. Deshalb empfehle ich dringend die Verwendung von ArrayList. – derivation

2

können Sie List l = Collections.synchronizedList(new ArrayList()); verwenden, wenn Sie Thread-sichere Version von ArrayList wollen.

0

http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html

Beachten Sie, dass diese Implementierung nicht synchronisiert ist. Wenn mehrere Threads gleichzeitig auf eine ArrayList-Instanz zugreifen und mindestens einer der Threads die Liste strukturell ändert, muss sie extern synchronisiert werden.

Da es intern keine Synchronisation gibt, ist das, was Sie theoretisieren, nicht plausibel.

So, Dinge aus der Synchronisation, mit unangenehmen und unvorhersehbaren Ergebnissen.

+0

Richtig, sah das. Ich hatte gedacht, dass eine 'ConcurrentModificationException' geworfen worden wäre, die nicht für uns geschieht. Vielleicht laufen wir in einen anderen Seiteneffekt, keine three safe Sammlung zu verwenden. –

+0

@Marcus, der scheint, nur der Fall zu sein, wenn Sie Ändern Sie die Liste, während Sie darüber iterieren. Außerdem gibt es keine Garantie, dass diese Ausnahme ausgelöst wird. – WhirlWind

1

java.util.concurrent verfügt über eine Thread-sichere Array-Liste. Die Standard-ArrayList ist nicht Thread-sicher und das Verhalten, wenn mehrere Threads gleichzeitig aktualisiert werden, ist nicht definiert. Es kann auch seltsame Verhalten mit mehreren Lesern geben, wenn ein oder mehrere Threads gleichzeitig schreiben.

3

Sie könnten auch eine null, eine ArrayOutOfBoundsException, oder etwas, das bis zur Implementierung übrig ist. HashMap Es wurde beobachtet, dass sie in Produktionssystemen in eine Endlosschleife gehen. Du musst nicht wirklich wissen, was schief gehen könnte, tu es einfach nicht.

Sie könnten Vector verwenden, aber es neigt zu arbeiten, die Schnittstelle ist nicht reich genug. Sie werden wahrscheinlich feststellen, dass Sie in den meisten Fällen eine andere Datenstruktur wünschen.

3

Das Verhalten ist wahrscheinlich nicht definiert, da ArrayList nicht threadsicher ist. Wenn Sie die Liste ändern, während ein Iterator darüber interagiert, erhalten Sie eine ConcurrentModificationException. Sie können die ArrayList mit Collection.synchronizedList umschließen oder eine threadsichere Auflistung verwenden (es gibt viele) oder einfach die add-Aufrufe in einen synchronisierten Block einfügen.

14

Es könnte eine beliebige Anzahl von Dingen passieren. Sie könnten beide Objekte korrekt hinzufügen. Sie können nur eines der Objekte hinzufügen. Sie könnten eine ArrayIndexOutOfBounds-Ausnahme abrufen, da die Größe des zugrunde liegenden Arrays nicht ordnungsgemäß angepasst wurde. Oder andere Dinge können passieren. Es genügt zu sagen, dass Sie sich nicht darauf verlassen können, dass irgendein Verhalten auftritt.

Als Alternativen könnten Sie Vector verwenden, Sie könntenverwenden, Sie könnten CopyOnWriteArrayList verwenden, oder Sie können eine separate Sperre verwenden. Alles hängt davon ab, was Sie sonst noch tun und welche Kontrolle Sie über den Zugriff auf die Sammlung haben.

3

Ich habe den folgenden Code entwickelt, um ein realistisches Szenario nachzuahmen.

100 Aufgaben werden parallel ausgeführt und sie aktualisieren ihren abgeschlossenen Status an das Hauptprogramm. Ich verwende eine CountDownLatch, um auf den Abschluss der Aufgabe zu warten.

import java.util.concurrent.*; 
import java.util.*; 

public class Runner { 

    // Should be replaced with Collections.synchronizedList(new ArrayList<Integer>()) 
    public List<Integer> completed = new ArrayList<Integer>(); 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     Runner r = new Runner(); 
     ExecutorService exe = Executors.newFixedThreadPool(30); 
     int tasks = 100; 
     CountDownLatch latch = new CountDownLatch(tasks); 
     for (int i = 0; i < tasks; i++) { 
      exe.submit(r.new Task(i, latch)); 
     } 
     try { 
      latch.await(); 
      System.out.println("Summary:"); 
      System.out.println("Number of tasks completed: " 
        + r.completed.size()); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     exe.shutdown(); 
    } 

    class Task implements Runnable { 

     private int id; 
     private CountDownLatch latch; 

     public Task(int id, CountDownLatch latch) { 
      this.id = id; 
      this.latch = latch; 
     } 

     public void run() { 
      Random r = new Random(); 
      try { 
       Thread.sleep(r.nextInt(5000)); //Actual work of the task 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      completed.add(id); 
      latch.countDown(); 
     } 
    } 
} 

Wenn ich die Anwendung 10 mal und mindestens 3 bis 4 mal lief das Programm nicht korrekte Anzahl der abgeschlossenen Aufgaben. Idealerweise sollte 100 gedruckt werden (wenn keine Ausnahmen vorkommen). Aber in einigen Fällen war es Drucken 98, 99 usw.

So beweist es, dass gleichzeitige Updates von ArrayList wird keine korrekten Ergebnisse geben.

Wenn ich die ArrayList durch eine synchronisierte Version ersetze, gibt das Programm die korrekten Ergebnisse aus.

0

Sie könnten statt ArrayList(); verwenden:

Collections.synchronizedList(new ArrayList()); 

oder

new Vector(); 

synchronizedList als von mir bevorzugt, weil es ist:

  • schneller auf 50-100%
  • können Arbeit mit bereits bestehenden Arra yList's
Verwandte Themen