2016-06-09 6 views
4

Ich muss wissen, wenn ich einen Synchronisierungsblock zu meinem Code hinzufügen sollte, wenn Sie ConcurrentHashMap verwenden. Sagen wir, ich habe eine Methode wie:Wann und wie sollte ich zusätzliche Synchronisation von ConcurrentHashMap verwenden?

private static final ConcurrentMap<String, MyObjectWrapper> myObjectsCache = new ConcurrentHashMap<>(CACHE_INITIAL_CAPACITY); 

    public List<MyObject> aMethod(List<String> ids, boolean b) { 
     List<MyObject> result = new ArrayList<>(ids.size()); 
     for (String id : ids) { 
      if (id == null) { 
       continue; 
      } 
      MyObjectWrapper myObjectWrapper = myObjectsCache.get(id); 
      if (myObjectWrapper == null) { 
       continue; 
      } 
      if (myObjectWrapper.getObject() instanceof MyObjectSub) { 
       ((MyObjectSub) myObjectWrapper.getObject()).clearAField(); 
       myObjectWrapper.getObject().setTime(System.currentTimeMillis()); 
      } 
      result.add(myObjectWrapper.getObject()); 
      if (b) { 
       final MyObject obj = new MyObject(myObjectWrapper.getObject()); 
       addObjectToDb(obj); 
      } 
     } 
     return result; 
    } 

Wie soll ich effizient diese Methode gleichzeitig machen? Ich denke, dass die "Get" ist sicher, aber sobald ich den Wert aus dem Cache und aktualisieren Sie die Felder des zwischengespeicherten Objekts - es kann Probleme geben, weil ein anderer Thread den gleichen Wrapper bekommen könnte und versuchen, das zugrunde liegende Objekt zu aktualisieren ... Sollte ich Synchronisation hinzufügen? Und wenn ja, sollte ich dann von "get" bis zum Ende der Schleifeniteration oder der gesamten Schleife synchronisieren?

Vielleicht könnte jemand einige spezifischere Leitlinien der ordnungsgemäßen und effizienten Nutzung von ConcurrentHashMap teilen, wenn einige weitere Operationen auf der Karte Schlüssel/Werte innerhalb von Schleifen usw. gemacht werden müssen ...

Ich würde wirklich dankbar sein.

EDIT: Einige Kontext für die Frage: arbeite ich zur Zeit auf Refactoring einiger dao Klassen in Produktionscode und einige der Klassen aus der Datenbank abgerufen HashMaps für das Caching von Daten verwendet. Alle Methoden, die den Cache nutzten (zum Schreiben oder Lesen), hatten ihren gesamten Inhalt in einem synchronisierten (Cache-) Block (spielen sicher?). Ich habe nicht viel Erfahrung mit Nebenläufigkeit und ich möchte diese Gelegenheit wirklich nutzen, um zu lernen. Ich habe naiv die HashMaps in ConcurrentHashMaps geändert und möchte nun die synchronisierten Bloocks dort entfernen, wo sie benötigt werden. Alle Caches werden zum Schreiben und Lesen verwendet. Die vorgestellte Methode basiert auf einer der Methoden, die ich geändert habe, und jetzt versuche ich zu lernen, wann und in welchem ​​Umfang ich synchronisiere. Die Methode clearAField ändert nur einen Wert eines der Felder des umbrochenen POJO-Objekts, und addObjectToDb versucht, das Objekt der Datenbank hinzuzufügen.

Ein anderes Beispiel wäre der Cache werden Nachfüllen:

public void findAll() throws SQLException{ 
    // get data from database into a list 
    List<Data> data=getAllDataFromDatabase(); 
    cacheCHM.clear(); 
    cacheCHM.putAll(data); 
} 

In dem Fall, dass ich die klare und putAll in einem synchronize (cacheCHM) Block, rechts setzen sollte?

Ich habe versucht, finden und einige Beiträge/Artikel über die richtige und effiziente Nutzung von CHM, aber die meisten handeln von Einzeloperationen, ohne Schleifen usw. lesen .... Das beste was ich gefunden habe, seien: http://www.javamadesoeasy.com/2015/04/concurrenthashmap-in-java.html

+0

Es hängt ziemlich von Ihrer Anwendungslogik ab. – pintxo

+2

Wenn Sie eine Aktualisierung des abgerufenen Werts als threadsicher durchführen müssen, müssen Sie die Synchronisierung für das Objekt selbst durchführen. 'ConcurrentHashMap' schützt nur die Struktur der Map selbst (dh die Beziehung von Schlüsseln zu Werten), nicht die enthaltenen Werte. –

+0

Ich stimme mit @Jim überein, gleichzeitige Karte schützt nur die Struktur (d. H. Beziehung von Schlüsseln zu Werten). Ich möchte noch eine weitere Sache hinzufügen. Laut dem obigen Code-Kontext lesen Sie nur den Wert ie.e myObjectsCache.get (id), so dass Sie zu diesem Zweck sogar keine Concurrent-Map benötigen, bis Sie anrufen map.put(). – pbajpai21

Antwort

1

Sie haben nicht erwähnt, welche Parallelität in Ihrer App erwartet wird. Daher gehe ich davon aus, dass mehrere Threads aMethod und nichts anderes aufrufen.

Sie haben nur einen Aufruf an die ConcurrentHashMap: myObjectsCache.get(id), das ist in Ordnung. Da nichts in Ihren ObjektCache schreibt [siehe obige Annahme], brauchen Sie nicht einmal eine ConcurrentHashMap! Sie würden mit jeder unveränderlichen Sammlung in Ordnung sein. Sie haben eine verdächtige Zeile am Ende: addObjectToDb(obj), beeinflusst diese Methode auch Ihren Cache? Wenn es so ist, ist es immer noch sicher (wahrscheinlich müssten wir die Methode sehen, um sicher zu sein), aber Sie brauchen definitiv die ConcurentHashMap.

Die Gefahr ist, wo Sie die Objekte zu ändern, hier:

myObjectWrapper.getObject().clearAField(); 
myObjectWrapper.getObject().setTime(System.currentTimeMillis()); 

Es ist möglich, dass mehrere Threads diese Methoden auf das gleiche Objekt in der gleichen Zeit zu nennen. Ohne zu wissen, was diese Methoden tun, können wir nicht sagen, ob das sicher ist oder nicht.Wenn diese Methoden beide als synchronisiert markiert sind oder wenn sichergestellt wurde, dass diese Methoden nicht gleichzeitig ausgeführt werden können, ist das kein Problem (aber Vorsicht, diese Methoden können in verschiedenen Reihenfolgen ausgeführt werden, was Sie intuitiv erwarten würden!). Wenn Sie nicht so vorsichtig waren, besteht die Gefahr der Datenbeschädigung.

+0

Entschuldigung, dass ich nicht sehr genau bin. Ich habe Cache-Maps in einigen meiner Dao-Klassen, die die Daten aus der SQL-Datenbank gespeichert speichern. Diese Caches werden geschrieben und gelesen, auch einige Manipulationen an den zwischengespeicherten Objekten können auftreten. Die Klassen verwendeten HashMaps für Caches und synchronisierten den gesamten Inhalt von Methoden, die den Cache mit einer Sperre für die Karte verwendeten. Ich möchte den Code effizienter gestalten und die Cache-Kohärenz beibehalten. Die Methoden clearAField und setTime aus dem Beispiel sind nicht synchronisiert. Dies sind einfache Setter eines Pojos. – ssr

+0

Der Umfang Ihrer Frage beginnt sehr breit zu klingen. Kurz gesagt, der einfachste Weg, Ihre Welt threadsafe zu machen, besteht darin, unveränderliche Objekte in gleichzeitig sicheren Containern zu verwenden. In dem Moment, in dem Sie beginnen, die Objekte in den Containern zu mutieren (ohne sie zu kopieren), wie Sie es hier tun, setzen Sie sich einer Reihe potenzieller Probleme aus. – Matthew

+0

Also sollte ich alle nicht-atomaren Operationen in meinem Cache synchronisieren und wenn ich gecachete Objekte nach retreiving ändern (entfernte Objekte zählen auch? Oder nur die "get"?) - Ich sollte diesen Code in den Sync-Block (ich don glaube ich nicht, dass ich die zwischengespeicherten Objekte unveränderlich machen könnte? Wie Sie sehen, suche ich jemanden, der mich an der Hand führt ... weil ich sicher sein muss, dass meine Änderungen gültig sind. – ssr

0

Ein besserer Ansatz für Threadsaftey und Caches ist die Verwendung von immutable objects. Hier ist, was die MyObjectSub calss aussehen könnte, wenn es waren unveränderlich [nicht sicher, warum Sie den Wrapper müssen - ich weglassen würde, dass vollständig möglich]:

//Provided by way of example. You should consider generating these 
//using http://immutables.github.io/ or similar 
public class MyImmutableObject { 
    //If all members are final primitives or immutable objects 
    //then this class is threadsafe. 
    final String field; 
    final long time; 

    public MyImmutableObject(String field, long time) { 
     this.field = field; 
     this.time = time; 
    } 

    public MyImmutableObject clearField() { 
     //Since final fields can never be changed, our only option is to 
     //return a copy. 
     return new MyImmutableObject("", this.time); 
    } 

    public MyImmutableObject setTime(long newtime) { 
     return new MyImmutableObject(this.field, newtime); 
    } 
} 

Wenn Ihre Objekte sind unveränderlich dann Sicherheitsfaden viel einfacher ist . Ihre Methode würde in etwa so aussehen:

public List<Result> typicialCacheUsage(String key) { 
    MyImmutableObject obj = myObjectsCache.get(key); 

    obj = obj.clearField(); 
    obj = obj.setTime(System.currentTimeMillis()); 

    //If you need to put the object back in the cache you can do this: 
    myObjectsCache.put(key, obj); 

    List<Result> res = generateResultFromObject(obj); 
    return res; 
} 
+0

Es wäre besser, wenn Sie dies zu Ihrer bestehenden Antwort hinzufügen, anstatt sie als separate zu veröffentlichen - an sich ist dies keine Antwort auf die Frage. – Hulk

Verwandte Themen