Java: Synchronisation auf primitive?
In unserem system, wir haben eine Methode, tun einige Arbeit, wenn es heißt, mit einer bestimmten ID:
public void doWork(long id) { /* ... */ }
Nun, diese arbeiten zu können, gleichzeitig für verschiedene IDs, aber wenn die Methode aufgerufen wird, mit der gleichen ID von 2 threads, ein thread gesperrt werden sollte, bis es fertig ist.
Die einfachste Lösung wäre, eine Karte zu haben, dass Karten aus der Langen ID zu einem beliebigen Objekt, können wir die Sperre auf. Ein problem, das ich sehe ist, dass Sie wir haben Tonnen von IDs im system, und diese Karte wird weiter wachsen, jeden Tag.
Ideal, ich denke, wir brauchen ein system, in dem wir jeder thread holt sich ein Objekt sperren, sperren Sie, wenn möglich, die Arbeit zu tun, dann signalisieren, dass wir fertig sind mit dem Schloss. Wenn klar ist, dass niemand anderes mit dieser besonderen sperren, dann kann sicher entfernen Sie es aus die Karte sperren zu verhindern, dass der memory-leak.
Ich mir vorstellen, das muss ein ziemlich häufiges Szenario, ich hoffe also, dass es eine bereits vorhandene Lösung gibt. Weiss jemand eine?
- Wie werden diese id-Nummern produziert? Wenn es eine Vermittlung von diesen IDs nur aus der hand-Objekte anstelle von primitiven, die Sie verwenden können, diejenigen, die für die Verriegelung/Synchronisation.
- Siehe auch stackoverflow.com/q/6616141/32453
Du musst angemeldet sein, um einen Kommentar abzugeben.
Erfand ich eine Sache wie das für mich vor einiger Zeit. Ich nenne es eine äquivalenz-Klasse lock, d.h. Sie sperren auf alle Dinge, die gleich sind, um der gegebenen Sache. Sie können es von meinem github, und es verwenden, unterliegt der Apache-2-Lizenz, wenn Sie möchten, oder einfach nur Lesen es und vergessen Sie es!
Können Sie versuchen, etwas mit einem ReentrantLock, so dass du eine
Map<Long,Lock>
. Jetzt nach lock.release() können Sie testen, zu sperren.hasQueuedThreads(). Wenn das false zurückgibt, können Sie entfernen Sie Sie aus der Karte.Können Sie versuchen, den folgenden kleinen 'hack'
ist 100% garantiert, um wieder den gleichen Instanz.
Den
UNIQUE_METHOD_PREFIX
, kann eine Feste Konstante, oder kann bezogen werden über:gewährleisten, dass die Sperre passiert nur auf dieser präzisen Methode. Das ist in Ordnung, um deadlocks zu vermeiden.
Long.valueOf()
können Sie nicht einfach eine negative gegossen, weil die Antwort eine detaillierte Erklärung und der VORBEHALT. Und in Bezug auf dieString.intern()
ich denke, ganz im Gegenteil. Es spart Platz, aber die schlechten machen aus der performance-Sicht.Beginnen mit:
Du redest hier über einen lock-striping setup. Das eine Ende des Kontinuums ist eine einzige Riesen-Schloss für alle ids, die ist einfach und sicher, aber nicht gleichzeitige. Das andere Ende wird ein Schloss pro-id, die ist leicht (um einige Grad) und sicher und sehr Auger aber könnte erfordern eine große Anzahl von "lock-in der Lage Objekte" im Speicher (wenn du nicht schon eine hast). Irgendwo in der Mitte ist die Idee der Schaffung einer Sperre für eine Reihe von ids - diese Option ermöglicht die Einstellung der Parallelität auf der Grundlage Ihrer Umgebung und Entscheidungen zu treffen, über Kompromisse zwischen Speicher und Parallelität.
ConcurrentHashMap kann verwendet werden, um dies zu erreichen, als CHM-aus intern der Segmente liegen (sub-maps) und es gibt einen lock-pro segment. Dies gibt Ihnen die Parallelität gleich der Anzahl von Segmenten (standardmäßig auf 16, ist aber konfigurierbar).
Gibt es eine Reihe von anderen möglichen Lösungen für die Partitionierung des ID-Raums und erzeugt Sätze von sperren, aber Sie sind Recht empfindlich zu sein die clean-up und memory-leak-Probleme - kümmern, die unter Beibehaltung der Parallelität ist ein schwieriges Geschäft. Benötigen Sie irgendeine Art von Referenz zählt auf jeden sperren und zu verwalten, die Räumung des alten Schlössern sorgfältig zu vermeiden, wenn eine Sperre, die in den Prozess der Arbeit gesperrt. Wenn Sie diesen Weg gehen, verwenden Sie ReentrantLock oder ReentrantReadWriteLock (und nicht synchronisierten Objekte) wie können Sie explizit verwalten das Schloss als ein Objekt und verwenden Sie die zusätzliche Methoden zur Verfügung.
Gibt es auch einige Sachen auf dieser und StripedMap Beispiel in Java Concurrency in Practice Abschnitt 11.4.3.
ConcurrentHashMap
verwenden möglicherweise weniger sperren, aber Sie brauchen nicht zu halten, eine für die gesamte Dauer derdoWork(id)
nennen.Ich würde sagen, du bist schon ganz schön weit, um eine Lösung. Eine
LockManager
wer faul und Referenz-gezählt-ly verwaltet die sperren für Sie. Verwenden Sie dann indoWork
:Object
mit einigen anderen benutzerdefinierten Klasse.Wäre es nicht genug, um eine SynchronizedHashMap oder Sammlungen.synchronizedMap(Map m) aus java.util.concurrent-Paket anstelle des einfachen HashMap, wo Aufrufe zum abrufen und einfügen sind nicht synchronisiert?
etwas wie:
Versuchen Sie es mit einer (synchronisierten) anzeigen.
Vielleicht, wenn es wächst zu groß, können Sie deaktivieren Sie Ihre Inhalte in regelmäßigen Abständen.
Dies ist, wo ich mit einem canonicalizing Karte, die braucht Ihr
long
input und gibt einen kanonischenLong
Objekt, das Sie dann verwenden können, um zu synchronisieren. Ich habe geschrieben über canonicalizing Karten hier; ersetzen Sie einfachString
durchLong
(und um Ihr Leben leichter machen, lassen Sie es nehmen Sie einelong
als parameter).Sobald Sie die canonicalizing Karte, Sie würden schreiben Ihr Schloss bewachten code wie folgt:
Den canonicalizing Karte würde sicherstellen, dass die gleichen
lockObject
zurückgegeben wird, für die gleiche ID. Wenn es keine aktiven Referenzen zulockObject
sind, werden Sie für die garbage collection freigegeben, so dass Sie nicht füllen Sie den Speicher mit unnötigen Objekten.Könnten Sie eine Liste erstellen oder eine Reihe von aktiven ids und verwenden Sie wait und notify:
Probleme gelöst:
Probleme gibt:
Ich vielleicht zu spät zum Spiel, aber diese Lösung ist nicht undicht Speicher und Sie müssen nicht daran denken, zu tun-lock-frei:
es zu verwenden, fügen Sie einfach eine Abhängigkeit:
Ich empfehlen, dass Sie die Dienstprogramme von java.util.gleichzeitige, vor allem die Klasse AtomicLong. Sehen Verwandte javadoc