2017-02-21 4 views
2

Ich habe Guava-Cache mit Speicher userId Mutex-Cache.Guava-Cache asMap-Methode

Cache<Long, Object> byUserIdMutex = CacheBuilder.newBuilder() 
     .concurrencyLevel(4) 
     .weakKeys() 
     .maximumSize(10000) 
     .expireAfterWrite(10, TimeUnit.MINUTES) 
     .build(); 



private Object getMutex(long userId) { 
    Object newLock = new Object(); 
    Object old = byUserIdMutex.asMap().putIfAbsent(userId, newLock); 
    if (old != null) { 
     return old; 
    } 
    return newLock; 
} 

Dann verwende ich synchronisierte Abschnitt mit Mutex-Objekt. Ich erwarte, dass derselbe Benutzer von verschiedenen Threads auf eine andere Aufgabe wartet, damit der gleiche Schlüssel vervollständigt wird.

Lasst uns sagen, wenn ich Faden 1

synchronized (getMutex(1)) { 
} 

haben dann 2 für 1-Thread wartet Thread-Ausführung verlassen synchronisiert, bevor fertig, aber es stellt sich heraus, dass es nicht geschieht, warten Fäden nicht miteinander .

Vielleicht habe ich ein Rennen bei der Umwandlung von Guava-Cache in Karte mit asMap() -Methode?

+2

Betrachten Sie stattdessen Guava 'Striped' zum Sperren, anstatt einen Cache. –

+0

@BenManes passt nicht für mich. Auch wenn ich Striped mit genügend Streifen (Locks) im Inneren erzeuge, kann es zu einer Kollision kommen, wenn verschiedene Benutzer auf einander warten. – user12384512

+0

Ein großer fauler schwacher Streifen ist eine schwach bewertete Karte. Das wäre also eine sicherere Räumungspolitik. –

Antwort

2

Verlassen Sie Ihren Verriegelungsmechanismus zur Seite (wie @BenManes mentioned in comment, ob Striped nicht better for your use case ist), sollten Sie LoadingCache hier verwenden:

LoadingCache<Long, Object> byUserIdMutex = CacheBuilder.newBuilder() 
     .concurrencyLevel(4) 
     .weakKeys() 
     .maximumSize(10000) 
     .expireAfterWrite(10, TimeUnit.MINUTES) 
     .build(CacheLoader.from(Object::new)); 

private Object getMutex(long userId) { 
    return byUserIdMutex.getUnchecked(userId); 
} 

So kann man keine Rennbedingungen haben wird, da getUnchecked Vertrag :

Gibt den mit dem Schlüssel in diesem Cache verbundenen Wert zurück, wobei dieser Wert bei Bedarf zuerst geladen wird. Kein beobachtbarer Zustand, der diesem Cache zugeordnet ist, wird geändert, bis der Ladevorgang abgeschlossen ist.

Plus, Methode getMutex wäre wahrscheinlich redundant.

+1

fyi, du kannst den Loader mit 'CacheLoader.from (function)' als Einzeiler schreiben, was gut mit Lambdas funktioniert. –

+0

@BenManes Danke, als ich den obigen Code schrieb, fragte ich mich, ob so etwas in Guava existieren sollte, und das tut es auch! Es unterstützt auch 'CacheLoader.from (supplier)', also passt 'Object :: new' auch gut. – Xaerxess

+0

Oddly 'from (supplier)' erzwingt einen 'Objekt'-Schlüssel, so traurig ist es nicht sehr nützlich, wie es ist. –