Ich möchte Methodenaufrufe auf Basis einer ID synchronisieren, d. H. So etwas wie eine Nebenläufigkeit Decorator einer bestimmten Objektinstanz.
Zum Beispiel:
Alle Threads, die die Methode mit dem Parameter "id1" aufrufen, sollten seriell zueinander ausgeführt werden.
Alle übrigen, die die Methode mit unterschiedlichem Argument aufrufen, sagen "id2", sollten parallel zu den Threads ausgeführt werden, die die Methode mit dem Parameter "id1" aufrufen, aber wiederum seriell zueinander.Feinkörnige Synchronisation/Sperren von Methodenaufrufen auf der Grundlage von Methodenparametern
Also in meinen Gedanken kann dies implementiert werden, indem eine Sperre (http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantLock.html) Instanz pro solche Methode param. Jedes Mal, wenn die Methode mit dem Parameter aufgerufen wird, wird die dem spezifischen Parameterwert (z. B. "id1") entsprechende Sperrinstanz nachgeschlagen, und der aktuelle Thread versucht, die Sperre zu erhalten.
in Code Sprechen:
public class ConcurrentPolicyWrapperImpl implements Foo {
private Foo delegate;
/**
* Holds the monitor objects used for synchronization.
*/
private Map<String, Lock> concurrentPolicyMap = Collections.synchronizedMap(new HashMap<String, Lock>());
/**
* Here we decorate the call to the wrapped instance with a synchronization policy.
*/
@Override
public Object callFooDelegateMethod (String id) {
Lock lock = getLock(id);
lock.lock();
try {
return delegate.delegateMethod(id);
} finally {
lock.unlock();
}
}
protected Lock getLock(String id) {
Lock lock = concurrentPolicyMap.get(id);
if (lock == null) {
lock = createLock();
concurrentPolicyMap.put(id, lock);
}
return lock;
}
}
protected Lock createLock() {
return new ReentrantLock();
}
Es scheint, dass das funktioniert - ich mit jmeter einige Performance-Tests haben und so weiter. Dennoch, wie wir alle wissen, Parallelität in Java ist eine knifflige Sache, habe ich beschlossen, um Ihre Meinung hier zu fragen.
Ich kann nicht aufhören zu denken, dass es einen besseren Weg geben könnte, dies zu erreichen. Zum Beispiel mit einer der BlockingQueue-Implementierungen. Was denken Sie?
Ich kann auch nicht wirklich sicher entscheiden, ob es ein mögliches Synchronisationsproblem mit der Sperre gibt, d. H. Die protected Lock getLock(String id)
Methode. Ich benutze eine synchronisierte Sammlung, aber ist das genug? I.e. sollte es nicht etwas sein, das wie folgt statt dessen, was ich gerade habe:
protected Lock getLock(String id) {
synchronized(concurrentPolicyMap) {
Lock lock = concurrentPolicyMap.get(id);
if (lock == null) {
lock = createLock();
concurrentPolicyMap.put(id, lock);
}
return lock;
}
}
Also was denkst du?
Sie sagen "Sperren Erstellungsfragen beiseite" ... Finden Sie irgendwelche oder meinen Sie, dass Sie nicht kommentieren? – Svilen
Wenn ich nun genauer hinschaue ... ja, dann würden Sie definitiv den synchronisierten Block in Ihrer Methode getLock() brauchen. Wie du es im zweiten Beispiel hast. Ansonsten rufen image (a) 5 Threads gleichzeitig getLock() für die gleiche ID auf; (b) sie alle finden die Sperre null; (c) sie alle erstellen und geben eine neue Sperrinstanz zurück. Scheitern. Der Synchronisierungsblock in Ihrem zweiten Bitcode vermeidet dies. – Keith
Danke, Keith. Übrigens, haben Sie vielleicht eine Idee, wie Sie das Gleiche machen können, aber mit Vollstreckern/Aufgaben. I.e. einen einzelnen ThreadPoolExecutor haben, aber die Task-Ausführung durch einen Schlüssel partitionieren. Zum Beispiel werden Aufgaben für den Schlüssel "id1" in serieller Weise zueinander ausgeführt, während gleichzeitig parallel zu allen anderen Aufgaben für Schlüssel "id2", "id3", etc ... – Svilen