2017-06-22 3 views
1

Normalerweise würde ich einen kritischen Abschnitt wie den folgenden sperren.Verwendung eines Schlüssels zum Synchronisieren des Zugriffs auf den Codeblock

public class Cache { 
    private Object lockObject = new Object(); 

    public Object getFromCache(String key) { 
     synchronized(lockObject) { 

      if (cache.containsKey(key)) { 
       // key found in cache - return cache value 
      } 
      else { 
       // retrieve cache value from source, cache it and return 
      } 
     } 
    } 
} 

Die Idee, den ich eine Race-Bedingung zu vermeiden, die in der Datenquelle getroffen zu mehreren Malen und der Schlüssel werden hinzugefügt, um den Cache mehrmals führen könnten.

Gerade jetzt, wenn zwei Threads in etwa zur gleichen Zeit für verschiedene Cache-Schlüssel kommen, werde ich immer noch eins blockieren.

Angenommen, die Schlüssel sind eindeutig - funktioniert das Schloss weiterhin, indem der Schlüssel gesperrt wird?

Ich denke, wird es nicht funktionieren, weil ich verstehe, dass die Objektreferenz die gleiche sein sollte, damit das Schloss in Kraft tritt. Ich denke, das kommt darauf an, wie es auf Gleichheit prüft.

public class Cache { 

    public Object getFromCache(String key) { 
     synchronized(key) { 

      if (cache.containsKey(key)) { 
       // key found in cache - return cache value 
      } 
      else { 
       // retrieve cache value from source, cache it and return 
      } 
     } 
    } 
} 
+0

Nein. Die Ressource, die Sie schützen müssen, ist der Cache, nicht der Schlüssel. Was Sie vorschlagen, macht keinen Sinn. – EJP

+1

In der Regel würde der Schlüssel in ein Array von Sperrobjekten hashen, um das Instanzproblem zu vermeiden. Dies wird als Striping bezeichnet, bei dem der Stripe (Ressource) durch diese Sperre vor möglichen Mutationen geschützt wird. In Ihrem Beispiel eines Caches wird dies Memoisierung genannt, um einen [Cache-Ansturm] (https://en.wikipedia.org/wiki/Cache_stampede) zu vermeiden, indem der interne Hash-Tabelleneintrag gesperrt wird. Um das Sperren eines Lesevorgangs zu vermeiden, würde dies ein optimistisches Get-and-Fallback (doppelt geprüftes Locking) verwenden. [Caffeine] (https://github.com/ben-manes/caffeine) und andere Caches implementieren diese Technik. –

+0

Danke Ben Ihre Erklärung war nützlich. Ich werde eine Reihe von Sperren vorstellen, die auf den Schlüssel gehackt sind. – Avner

Antwort

-1

Jedes Objekt verfügt über einen impliziten Monitor, auf dem die Synchronisation funktioniert. String-Objekte können in Heap erstellt werden und können auch für denselben Zeichensatz unterschiedlich sein (wenn sie mit new erstellt wurden) oder aus dem Pool stammen. Zwei Threads greifen nur dann auf den kritischen Abschnitt mit synchronisiertem Block zu, wenn sie sich auf demselben Objekt synchronisieren.

Synchronisieren auf String Literal ist wirklich eine schlechte Idee. String-Literal aus Pool werden gemeinsam verwendet. Stellen Sie sich vor, wenn Sie an zwei verschiedenen Stellen Ihres Codes zwei synchronisierte Abschnitte haben und Sie auf zwei Referenzen von String synchronisieren, aber mit String mit demselben Satz von Zeichen initiieren, wenn String aus Pool verwendet wird, dann beide Orte das gleiche Objekt . Auch wenn beide Orte unterschiedliche geschäftliche Kontexte haben, können Sie dennoch in Ihrer Anwendung gehängt werden. Es wird sehr schwierig sein, auch zu debuggen.

Für den spezifischen zu der Frage des Willens wird der Zweck gelöst, wenn Synchronisierung auf Schlüsseln erfolgt.

Sie möchten vermeiden, dass zwei Threads versuchen, zu schreiben, ohne den neuesten Wert des Caches zu lesen. Sie werden für jeden Eintrag einen anderen Schlüssel haben. Angenommen, thread1 und thread2 möchten auf denselben Schlüssel zugreifen, verhindert die Synchronisation auf demselben Schlüsselobjekt, dass beide in den synchronisierten Block eintreten. Wenn ein Thread3 auf einen anderen Schlüssel zugreifen möchte, kann dies sehr gut sein. Hier sehen wir, dass die Lese- und Schreibvorgänge im Vergleich zu einem gemeinsamen Objekt für Lese- und Schreibvorgänge für alle Schlüssel schneller sind. So weit so gut, aber das Problem wird auftreten, wenn Sie annehmen, dass Sie ein Array oder eine andere ähnliche nicht Thread-sichere Datenstruktur zum Speichern der zwischengespeicherten Werte behalten. Simultane Schreibvorgänge (für zwei oder mehr verschiedene Schlüssel) können dazu führen, dass ein Schreibvorgang von einem anderen Schreibvorgang mit demselben Index überschrieben wird.

Es hängt also von der Implementierung der Cache-Datenstruktur ab, wie Sie sie am besten für schnelleres Lesen und Schreiben in einer Multi-Threaded-Umgebung vorbereiten können.

+0

Ich bin mir nicht sicher, warum dies abgelehnt wurde, aber ich denke, es bestätigt meine Erwartung, dass die Instanz die gleiche sein muss. – Avner

+1

@awi Ich habe es abgelehnt, weil es das Problem nicht angeht, was ich in meinem Kommentar angegeben habe. Sie würden sich irren, um dies als eine richtige Antwort auf das, was Sie tatsächlich gefragt haben, zu betrachten. – EJP

+0

@avvi Bis zu einem gewissen Grad stimme ich EJP zu, nachdem ich es überarbeitet hatte, stellte ich fest, dass ich einige Dinge vorher vermisste. Ich habe die Antwort bearbeitet und versucht, die fehlenden Bereiche zuvor zu berühren. –

Verwandte Themen