2016-06-08 4 views
0

Mein Haupt spawnt 2 Threads und beide müssen auf die gleiche Liste zugreifen. Ich bin nicht sicher, was der beste Weg ist, dies zu tun. Hier ist, was ich habe, aber ich laufe immer noch in concurrentModificationException.Wie kann ich auf diese Liste zugreifen und auch Thread-sicher sein?

class Parent { 
    private List<String> data; 

    public List<String> getData() { 
     return data; 
    } 

    public static void main(String args[]) { 
     Parent p = new Parent(); 
     p.start(); 
    } 

    public void start() { 
     Thread a = new Thread(new A(this)).start(); 
     Thread b = new Thread(new B(this)).start(); 
    } 

    public A implements Runnable { 
     private Parent parent; 

     public A(Parent p) { 
     parent = p; 
     } 

     public void run() { 
     while (true) { 
      parent.getData().add("data"); 
     } 
     } 
    } 

    public B implements Runnable { 
     private Parent parent; 

     public B(Parent p) { 
     parent = p; 
     } 

     public void run() { 
     Iterator<String> i = parent.getData().iterator(); 
     while(i.hasNext()) { 
      // do more stuff with i 
      i.remove(); 
     } 
     } 
    } 
} 

meine A Klasse ist im Grunde ein Produzent von Daten und B ist der Verbraucher. Ich akzeptiere die Möglichkeit, dass ich das falsch mache. So ist jede Hilfe willkommen. Ich muss nur sicher in der Lage sein, zu einer Liste von einem Thread hinzuzufügen und ein Element aus der Liste von einem anderen Thread zu entfernen. Danke im Voraus.

+4

Verwenden Sie keine Liste anstelle einer Warteschlange. Im [java.util.concurrent] (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html) gibt es zahlreiche Utilities, die für Producer/Consumer-Operationen gedacht sind) Paket. – shmosel

+4

Sie möchten wahrscheinlich eine BlockingQueue, keine Liste. –

+0

cool krank in diese schauen. Wenn ich die Liste austausche, sollte der Rest theoretisch in Ordnung sein? – Dagron

Antwort

4

Nun, für einen Hersteller/Verbraucher würde ich LinkedBlockingQueue oder ConcurrentLinkedQueue empfehlen. Dies behandelt gleichzeitige Lese- und Schreibvorgänge (oder Push/Abfragen in diesem Fall).

Wahrscheinlich möchten Sie, dass Ihr Consumer läuft, bis einige Shutdown-Bedingungen an ihn gesendet werden. Wenn Sie eine Sperrwarteschlange verwenden, bedeutet dies, dass Sie ein Element in der Warteschlange senden möchten, das angibt, dass der Verbraucher den Konsum stoppen soll. Dies wäre eine blockierende Warteschlangenimplementierung mit einem Herunterfahren.

public enum QueueItemType { 
     CONSUMABLE, 
     SHUTDOWN 
    } 

    public class QueueItem { 
     public final QueueItemType type; 
     public final String payload; 

     public QueueItem(QueueItemType type, String payload) { 
     this.type = type; 
     this.payload = payload; 
     } 
    } 

    public class B implements Runnable { 
     private Parent parent; 

     public B(Parent p) { 
     parent = p; 
     } 

     public void run() { 
     while(true) { 
      QueueItem data = parent.getData().poll(); 
      if (data.type == QueueItemType.SHUTDOWN) { 
       break; 
      } else { 
       // do more stuff with data.payload 
      } 
     } 
     } 
    } 

Hinweis, dass es keine Nullprüfung für eine poll Ergebnis der Blockierung Warteschlange. Dies liegt daran, dass blockierende Warteschlangen definitionsgemäß den laufenden Thread blockieren, bis etwas vorhanden ist.

Wenn Sie einen Verbraucher bevorzugen, der nicht mit dem Hersteller konkurriert, müssen Sie regelmäßig abfragen und den Consumer-Thread ausschlafen. Hier ist ein Beispiel, wenn Sie die ConcurrentLinkedQueue verwendet:

public class B implements Runnable { 
     private Parent parent; 

     public B(Parent p) { 
     parent = p; 
     } 

     public void run() { 
     while(parent.isStillRunning()) { 
      String data = parent.getData().poll(); 
      if (data != null) { 
       // do more stuff with data 
      } else { 
       Thread.sleep(10 /*10 ms, but you can make this whatever poll interval you want*/); 
      } 
     } 
     } 
    } 
+0

Verwenden Sie Thread.sleep() nicht dafür. Beenden Sie stattdessen die Warteschlange, indem Sie ein flüchtiges Flag setzen und den Consumer-Thread unterbrechen oder indem Sie eine "Giftpille" in die Warteschlange setzen. Sonst großartige Antwort. – dnault

+0

Fühlen Sie sich frei, die Antwort zu verbessern :). Der "Poison Pill" -Ansatz würde definitiv mit einer blockierenden Warteschlange funktionieren. Der Vorteil einer ConcurrentLinkedQueue besteht darin, dass es keine Konflikte zwischen dem Producer und dem Consumer gibt, was eine wirklich coole Eigenschaft des zugrunde liegenden Algorithmus ist. Das Thread-Polling kostet zwar etwas CPU, aber der Produzent wird niemals vom Verbraucher ausgesperrt. – Chill

+0

Ich werde in diese "Giftpille" schauen, ich muss vermeiden, so viel wie möglich zu schlafen – Dagron

0

Die am wenigsten wirkungsvolle Veränderung, könnte eine Setter zu verwenden, die synchronisiert ist. Auf diese Weise müsste ein Thread warten, bis die Sperre aufgehoben wird, bevor die Sammlung hinzugefügt werden kann.

Verwandte Themen