Der Thread von Java - KeyListener bewirkt eine gleichzeitige Änderung. Lösung?

  • Meine Spieleaktualisierungsschleife durchläuft ständig ein ArrayList. Ich habe auch ein KeyListener. Wenn ein Button gedrückt wird, fügt er diesem ArrayList ein ArrayList hinzu eine gleichzeitige Modifikationsausnahme verursachen.

    Nach langem Suchen entschied ich, dass es das Beste wäre, zu versuchen (ich bin relativ neu in Java), einen Weg zu finden, um Schlüssel zusammenzuführen Ereignisse in den Haupt-Thread. Wie kann ich das in diesem Szenario tun, oder gibt es eine Möglichkeit, die ich nicht sehe? Vielen Dank.

    PS: Ich könnte nach fortgeschritteneren Wegen suchen, aber ich würde es lieber Single-Threading machen und die Schlüsselereignisse weitergeben zum Hauptthread.

    22 November 2011
    CésarClint Carter
3 answers
  • Ich gehe davon aus, dass Sie eine Ereignisschleife ausführen, zu der Sie am Ende des Ereignisses Ereignisse hinzufügen möchten. Wenn dies zutrifft, möchten Sie wahrscheinlich eine java.util.Queue-Implementierung verwenden, z. B. eine LinkedList, und eine

     while((event=queue.poll()) != null) { 
      /* process event */ 
      queue.add(keyEvent);
    }
     
    ausführen
    22 November 2011
    Nialscorva
  • LÖSUNG 1:

    Denken Sie daran, dass Ihr Schlüssel-Listener während des Iterationsvorgangs blockiert wird (das bedeutet auch) Ihr Iterator wird wie bei Lösung 2 mit alten Daten ausgeführt.

    Wenn Sie ArrayList verwenden, geben Sie Folgendes ein:

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

    Und verwenden Sie dieses list -Objekt überall. Sie müssen importieren

     import java.util.Collections;
     

    Ich stimme @msandiford zu. Hinzufügen, wie zu iterieren ....

     while(some_condition) {
    do something
      synchronized(list) {
        Iterator i = list.iterator();
        while (i.hasNext())
            do_something_with(i.next());
      }
      do something else
    }
     

    Dies ist eine schnelle Lösung, wenn Sie eine äußere Schleife haben, in der Sie iterieren. Damit sind Sie für einige Zeit aus dem Block synchronized, so dass KeyListener zur Array-Liste hinzugefügt werden kann.

    LÖSUNG 2:

    Wenn Sie COW verwenden möchten, beachten Sie, dass bei jedem Hinzufügen / Aktualisieren eine Kopie der darunter liegenden Auflistung erstellt wird und Ihr Iterator die Änderung nicht erkennt. Der Tastenlistener wird jedoch NICHT gesperrt (aber zu diesem Zeitpunkt wird eine neue Kopie darunter erstellt).

     import java.util.concurrent.CopyOnWriteArrayList;
    
    
    List list = new CopyOnWriteArrayList<your_object_type>();
     

    Für die Iteration:

     while(some_condition) {
      do something
      Iterator i = list.iterator();
      while (i.hasNext())
        do_something_with(i.next());
    
      do something else
    }
     

    LÖSUNG 3:

    Dies wird eine geringfügige Designänderung sein. Sie ähnelt Lösung 2, ist jedoch nur sinnvoll, wenn Sie nur Vorgänge hinzufügen. Was Sie also tun können, ist ein anderes Tempo List zu erstellen und in Keylistener dieser Liste hinzuzufügen. Sobald Ihre Iteration durch ist, machen Sie synchronized blockieren und verschieben Sie alle Objekte von Temp List nach List, die Sie für die Iteration verwenden. Dies blockiert NICHT Ihren KeyListener, aber der Iterator wird alte Daten wie in Lösung 2 sehen. Er könnte im Vergleich zu Lösung 2 eine bessere Leistung aufweisen.

    Wählen Sie also die Lösung, die für Ihr Design sinnvoll ist

    Ref:

    Java-Sammlungen COW

    22 November 2011
    havexz
  • Für eine Ereigniswarteschlange verwende ich im Allgemeinen ConcurrentLinkedQueue als Warteschlange passt wirklich gut in dieses Modell (viel besser als eine List / ArrayList).

    Eine Warteschlange Es ist viel einfacher, diesen Thread zu verwenden, da die Warteschlange nicht "listig" im Sinne einer Liste "durchlaufen" wird: Einfach ein Element drücken, ein Element greifen. Beide Operationen können atomar sein (und relativ einfach zu synchronisieren).

    Ich ziehe es vor, an dieses Modell mit der Annahme / Anforderung heranzukommen, dass ein Objekt einmal aus der Warteschlange verbraucht werden kann als "im Besitz" dieses Threads (der Thread, der das Objekt dort ablegt, sollte das Objekt aufgeben, bis es das Objekt über eine andere Warteschlange zurückerhält, wenn überhaupt).

    ConcurrentLinkedQueue:

    Eine unbeschränkte, Thread-sichere Warteschlange, die auf verknüpften Knoten basiert. Diese Warteschlange ordnet Elemente FIFO (first-in-first-out) an ... Eine ConcurrentLinkedQueue-Option ist eine geeignete Wahl, wenn viele Threads Zugriff auf eine gemeinsame Sammlung haben [aber sie funktionieren "für 2] ...

    Die CopyOnWriteArrayList ist eine andere Struktur, die verwendet werden könnte, obwohl es sich eher um eine "allgemeine" Struktur handelt. Ich bin mir nicht sicher, was "schneller" ist, aber wenn man bedenkt, dass ich mit ConcurrentLinkedQueue noch nie ein Problem hatte, ist dies aus obigen Gründen meine erste Wahl. Es gibt auch verschiedene Warteschlangenimplementierungen mit gleichzeitiger Sicherheit, einschließlich gebundener Warteschlangen, die auch im Gleichzeitiges -Paket.

    Um in" old school "zu bleiben, synchronisieren Sie einfach für dasselbe Objekt wenn 1) das Colle geändert (oder iteriert) wird

    22 November 2011