2008-10-16 4 views
7

Ich habe ein List-Objekt, auf das mehrere Threads zugreifen. Es gibt meistens einen Thread und unter bestimmten Bedingungen zwei Threads, der die Liste aktualisiert. Es gibt ein bis fünf Threads, die aus dieser Liste lesen können, abhängig von der Anzahl der Benutzeranforderungen, die verarbeitet werden. Die Liste ist keine Warteschlange für auszuführende Aufgaben, sondern eine Liste von Domänenobjekten, die gleichzeitig abgerufen und aktualisiert werden.Der beste Ansatz zur Verwendung in Java 6 für eine Liste, auf die gleichzeitig zugegriffen wird

Nun gibt es mehrere Möglichkeiten, den Zugriff auf diese Liste Thread-sicher zu machen:
-use synchronisierten Block
-use normalen Schloss (dh Lesen und Schreiben ops teilen gleiche Schloss)
-use ReadWriteLock
-Nutzung eines der neuen ConcurrentBLABLBA Sammlung Klassen

Meine Frage:
Was ist der optimale Ansatz, da die kritischen Abschnitte in der Regel nicht viele Operationen enthalten (meist nur das Hinzufügen/Entfernen/Einfügen oder das Abrufen von Elementen aus der Liste)?
Können Sie einen anderen Ansatz empfehlen, der oben nicht aufgeführt ist?

Einige Constraints
-optimale Leistung entscheidend ist, die Speichernutzung nicht so viel
-es muss eine geordnete Liste sein ( zur Zeit auf einem Arraylist synchronisieren), obwohl nicht eine sortierte Liste (dh mit nicht sortiert Comparable oder Comparator, aber nach dem Anzeigenauftrag)
-Die Liste ist groß, enthält bis zu 100000 Domain-Objekte, so dass etwas wie CopyOnWriteArrayList nicht praktikabel
-Die Schreiben/Update ciritical Abschnitte sind in der Regel sehr schnell, einfach zu tun/entfernen/einfügen oder ersetzen (setzen)
-die Leseoperationen werden in den meisten Fällen einen elementAt (index) -Aufruf ausführen, obwohl einige Leseoperationen eine binäre Suche ausführen können, oder indexOf (element) -keine direkte Iteration über die Liste erfolgt, obwohl Operation wie indexOf (..) wird die Liste durchlaufen

Antwort

3

Müssen Sie eine fortlaufende Liste verwenden? Wenn eine Map-Typ-Struktur geeigneter ist, können Sie eine ConcurrentHashMap verwenden. Mit einer Liste ist ein wahrscheinlich der effektivste Weg.

Bearbeiten, um OP-Bearbeitung zu reflektieren: Binäre Suche in Anzeigenauftrag? Speichern Sie einen Zeitstempel und verwenden Sie diesen zum Vergleich in Ihrer Binärsuche? Ist dies der Fall, können Sie möglicherweise den Zeitstempel als Schlüssel und ConcurrentSkipListMap als Container verwenden (der die Schlüsselreihenfolge beibehält).

+0

Ich mag die ConcurrentSkipListMap-Idee. In 90% der Fälle ist die Liste nach einem bestimmten Zeitstempel (Teil der ID jedes Domänenobjekts) sortiert, daher lohnt es sich, sie zu optimieren. Werde immer noch an die anderen 10% denken. –

1

Was machen die Lese-Threads? Wenn sie über die Liste iterieren, müssen Sie wirklich sicherstellen, dass niemand die Liste während des gesamten Iterationsprozesses berührt, sonst könnten Sie sehr seltsame Ergebnisse erhalten.

Wenn Sie genau definieren können, welche Semantik Sie benötigen, sollte es möglich sein, das Problem zu lösen - aber Sie werden wahrscheinlich feststellen, dass Sie Ihren eigenen Sammlungstyp schreiben müssen, um es richtig und effizient zu machen. Alternativ kann CopyOnWriteArrayList gut genug sein - wenn potenziell teuer. Grundsätzlich gilt, je mehr Sie Ihre Anforderungen festlegen können, desto effizienter kann es sein.

+0

Ich habe mir die CopyOnWriteArrayList angeschaut, aber das wäre zu teuer. Die Liste kann potenziell 100000 Elemente enthalten und wird sehr oft aktualisiert. –

+2

Okay, in diesem Fall müssen Sie Ihre Semantik sorgfältig ausarbeiten. Führen diese Aktualisierungen Einfügungen/Löschungen durch oder ersetzen sie nur Elemente? Wenn sie hinzufügen/löschen, tun sie dies am Anfang/Ende der Liste? (In diesem Fall könnte Ihnen eine verkettete Liste wirklich helfen.) –

1

Ich weiß nicht, ob dies eine posible Lösung für das Problem ist, aber ... es macht Sinn für mich einen Datenbank-Manager zu verwenden, die große Menge an Daten zu halten und lassen Sie es die Transaktionen verwalten

+0

Die Daten werden auf der Server-Seite vom DB-Manager und der Anwendungsserver-Ebene verwaltet, aber wir müssen sie dem Endbenutzer irgendwie anzeigen.Das Management dieser Liste findet auf der Clientseite statt, wo die Daten abgerufen werden. –

1

I zweite Telcontar's suggestion einer Datenbank, da sie tatsächlich für die Verwaltung dieser Datenskala und das Aushandeln zwischen Threads entwickelt wurden, während In-Memory-Sammlungen dies nicht tun.

Sie sagen, dass die Daten in einer Datenbank auf dem Server sind, und die lokale Liste auf den Clients ist der Benutzeroberfläche zuliebe. Sie müssen nicht alle 100000 Elemente auf dem Client auf einmal speichern oder solche komplizierten Änderungen daran vornehmen. Es scheint mir, dass Sie auf dem Client einen leichten Cache auf der Datenbank haben wollen.

Schreiben Sie einen Cache, der nur die aktuelle Teilmenge der Daten gleichzeitig auf dem Client speichert. Dieser Client-Cache führt keine komplexen Multithread-Bearbeitungen für seine eigenen Daten durch. Stattdessen werden alle Änderungen an den Server weitergeleitet und auf Aktualisierungen gewartet. Wenn sich Daten auf dem Server ändern, vergisst der Client einfach alte Daten und lädt sie erneut. Nur ein bestimmter Thread darf die Sammlung selbst lesen oder schreiben. Auf diese Weise spiegelt der Client einfach die auf dem Server stattfindenden Änderungen wider, anstatt komplizierte Änderungen selbst vorzunehmen.

Ja, das ist eine ziemlich komplizierte Lösung. Die Komponenten davon sind:

  • Ein Protokoll eine Reihe von Daten zum Laden, sagen Artikel 478.712-478.901, anstatt die ganze Sache
  • Ein Protokoll für Updates über geänderte Daten
  • Ein Cache-Klasse empfangen die Elemente nach ihrem bekannten Index auf dem Server speichert
  • Ein Thread, der zu diesem Cache gehört, der mit dem Server kommuniziert hat. Dies ist der einzige Faden, der auf die Sammlung schreibt selbst
  • Ein Thread zu diesem Cache gehören, die Rückrufe verarbeitet, wenn Daten
  • Eine Schnittstelle abgerufen wird, die UI-Komponenten implementieren, sie zu ermöglichen, Daten zu empfangen, wenn es
  • geladen wurde

auf den ersten Stich, die Knochen dieses Caches könnte wie folgt aussehen:

class ServerCacheViewThingy { 
    private static final int ACCEPTABLE_SIZE = 500; 
    private int viewStart, viewLength; 
    final Map<Integer, Record> items 
      = new HashMap<Integer, Record>(1000); 
    final ConcurrentLinkedQueue<Callback> callbackQueue 
      = new ConcurrentLinkedQueue<Callback>(); 

    public void getRecords (int start, int length, ViewReciever reciever) { 
     // remember the current view, to prevent records within 
     // this view from being accidentally pruned. 
     viewStart = start; 
     viewLenght = length; 

     // if the selected area is not already loaded, send a request 
     // to load that area 
     if (!rangeLoaded(start, length)) 
      addLoadRequest(start, length); 

     // add the reciever to the queue, so it will be processed 
     // when the data has arrived 
     if (reciever != null) 
      callbackQueue.add(new Callback(start, length, reciever)); 
    } 

    class Callback { 
     int start; 
     int length; 
     ViewReciever reciever; 
     ... 
    } 

    class EditorThread extends Thread { 

     private void prune() { 
      if (items.size() <= ACCEPTABLE_SIZE) 
       return; 
      for (Map.Entry<Integer, Record> entry : items.entrySet()) { 
       int position = entry.key(); 
       // if the position is outside the current view, 
       // remove that item from the cache 
       ... 
      } 
     } 

     private void markDirty (int from) { ... } 

     .... 
    } 

    class CallbackThread extends Thread { 
     public void notifyCallback (Callback callback); 
     private void processCallback (Callback) { 
      readRecords 
     } 
    } 
} 

interface ViewReciever { 
    void recieveData (int viewStart, Record[] records); 
    void recieveTimeout(); 
} 

Es gibt eine Menge Detail ist man für sich selbst füllen werde, offensichtlich.

+0

Vielen Dank für Ihre Antwort. Die bis zu 100000 Elemente sind die "Seite" der Daten, die wir dem Kunden entnehmen. Die Datenbank kann Milliarden von Einträgen enthalten. Was ich ernsthaft in Betracht ziehen würde, ist Ihre Empfehlung, auf die Liste mit nur einem Thread zuzugreifen. Dies würde die Dinge definitiv vereinfachen. –

1

können Sie einen Wrapper verwenden, die Synchronisation implementiert:

import java.util.Collections; 
import java.util.ArrayList; 

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

// make sure you only use syncList for your future calls... 

Dies ist eine einfache Lösung. Ich würde dies versuchen, bevor ich auf kompliziertere Lösungen zurückgreife.

Verwandte Themen