8

Java Gurus,ConcurrentHashMap vs ReentrantReadWriteLock basierten Custom Map für Nachladen

Zur Zeit haben wir eine HashMap<String,SomeApplicationObject> die häufig und gelegentlich modifiziert gelesen wird und wir Probleme haben, dass während der Änderung/Nachladen, lesen Betrieb zurückkehrt null was nicht akzeptabel ist.

Um dies zu beheben Ich habe folgende Möglichkeiten:

A. Verwendung ConcurrentHashMap

, die wie die erste Wahl aussieht, aber die Operation, die wir im Begriff ist, reload() sprechen sind - bedeutet clear() von replaceAll() gefolgt. Also, wenn die Map gelesen wird nach clear() und vor replaceAll() gibt es Null, was nicht wünschenswert ist. Auch wenn ich synchronize dies behebt das Problem nicht.

B. eine weitere Implementierung erstellen, basierend auf ReentrantReadWriteLock

Wo ich acquire Write Lock vor reload() Betrieb schaffen würde. Dies scheint passender zu sein, aber ich denke, da muss schon etwas vorhanden sein und ich muss das Rad nicht neu erfinden.

Was ist der beste Ausweg?

BEARBEITEN Ist eine Sammlung bereits mit einer solchen Funktion verfügbar?

+0

Ihre Karten sind praktisch unveränderbar, was sie zu einem perfekten Ziel für Veröffentlichungen über 'volatile' macht. –

+0

** Effektiv unveränderlich ** Entschuldigung, ich verstehe nicht unveränderlich so gut ... Könnten Sie bitte einige Hinweise geben ??? – SiB

Antwort

4

Es scheint, dass Sie nicht sicher sind, wie das, was Peter Lawrey vorschlägt, implementiert werden kann. Es könnte wie folgt aussehen:

class YourClass { 
    private volatile Map<String, SomeApplicationObject> map; 

    //constructors etc. 

    public void reload() { 
     Map<String,SomeApplicationObject> newMap = getNewValues(); 
     map = Collections.unmodifiableMap(newMap); 
    } 
} 

Es gibt keine Parallelitätsprobleme, weil:

  • Die neue Karte über eine lokale Variable erstellt wird, die per Definition nicht mit anderen geteilt - getNewValues nicht synchronisiert werden müssen oder Atom
  • die Zuordnung zu map ist Atom
  • map flüchtig ist, was garantiert, dass andere Threads die Änderung sehen
8

Da Sie die Karte neu laden, würde ich es bei einem Neuladen ersetzen.

Sie können dies tun, indem Sie eine flüchtige Map verwenden, die Sie bei der Aktualisierung vollständig ersetzen.

+0

Könnten Sie bitte mehr Details erklären? Es ist irgendwie schwer für mich zu verstehen, warum das funktioniert. Ich verstehe, dass volatile Sichtbarkeit garantieren wird, aber wie eine zusammengesetzte Operation wie clear als replaceAll funktioniert? Danke. – Eugene

+0

Um die gesamte Map zu ersetzen, müssen Sie die neue Map nur der alten flüchtigen Referenz in Ihrem Feld zuweisen, wenn Sie die neue Kopie erstellt haben. Das heißt, Sie ersetzen wirklich alle, sogar die Karte selbst, nicht nur ihren Inhalt. –

5

Das klingt ein viel wie Guava'sCache, obwohl es hängt wirklich davon ab, wie Sie die Karte sind bevölkern, und wie Sie die Werte berechnen. (Offenlegung: Ich trage zu Guava bei.)

Die eigentliche Frage ist, ob Sie angeben können, wie Sie Ihre mit der Eingabe String berechnen. Nur auf dem, was Sie uns bisher gesagt haben, könnte es etwa so aussehen ...

LoadingCache<String, SomeApplicationObject> cache = CacheBuilder.newBuilder() 
    .build(
     new CacheLoader<String, SomeApplicationObject>() { 
     public SomeApplicationObject load(String key) throws AnyException { 
      return computeSomeApplicationObject(key); 
     } 
     }); 

Dann, wenn Sie den Cache wieder aufbauen wollte, die Sie gerade cache.invalidateAll() nennen. Mit einem LoadingCache können Sie dann cache.get(key) aufrufen und wenn es den Wert nicht bereits berechnet hat, wird es neu berechnet. Oder vielleicht nach dem Anruf cache.invalidateAll(), können Sie cache.loadAll(allKeys) aufrufen, obwohl Sie immer noch in der Lage sein müssen, einzelne Elemente auf einmal zu laden, falls irgendwelche Fragen zwischen den invalidateAll und loadAll kommen.

Wenn das nicht akzeptabel ist - wenn Sie nicht einen Wert einzeln laden können, müssen Sie sie alle auf einmal laden - dann würde ich mit Peter Lawreys Ansatz fortfahren - behalten Sie einen volatile Verweis auf a map (im Idealfall ImmutableMap), berechnen Sie die gesamte Karte neu und weisen Sie die neue Karte der Referenz zu, wenn Sie fertig sind.

+0

SomeApplicationObject ist eine Entität, die aus der Datenbank geladen wird, wenn die Anwendung gestartet wird und ständig von Map gelesen wird ... Gelegentlich kann System Admin die Werte neu laden. – SiB

Verwandte Themen