Gleichzeitiger Zugriff auf Byte-Arrays in Java mit möglichst wenigen Sperren
Ich versuche, die Speichernutzung für die Sperrobjekte segmentierter Daten zu reduzieren. Siehe meine FragenHie undHie. Oder nehmen Sie einfach an, Sie haben ein Bytearray und alle 16 Bytes können in ein Objekt (de) serialisiert werden. Nennen wir dies eine "Zeile" mit einer Zeilenlänge von 16 Bytes. Wenn Sie nun eine solche Zeile von einem Writer-Thread ändern und von mehreren Threads lesen, müssen Sie sie sperren. Und wenn Sie eine Byte-Array-Größe von 1 MB (1024 * 1024) haben, bedeutet dies 65536 Zeilen und die gleiche Anzahl von Sperren.
Das ist ein bisschen zu viel, außerdem brauche ich viel größere Bytearrays und möchte es auf etwas reduzieren, das in etwa proportional zur Anzahl der Threads ist. Meine Idee war es, ein @ zu erstell
ConcurrentHashMap<Integer, LockHelper> concurrentMap;
woInteger
ist der Zeilenindex und bevor ein Thread eine Zeile 'betritt', fügt er ein Sperrobjekt in diese Map ein (ich habe diese Idee vondiese Antwort). Aber egal, was ich überlege, ich kann keinen Ansatz finden, der wirklich threadsicher ist:
// somewhere else where we need to write or read the row
LockHelper lock1 = new LockHelper();
LockHelper lock = concurrentMap.putIfAbsent(rowIndex, lock1);
lock.addWaitingThread(); // is too late
synchronized(lock) {
try {
// read or write row at rowIndex e.g. writing like
bytes[rowIndex/16] = 1;
bytes[rowIndex/16 + 1] = 2;
// ...
} finally {
if(lock.noThreadsWaiting())
concurrentMap.remove(rowIndex);
}
}
Sehen Sie eine Möglichkeit, diesen Thread sicher zu machen?
Ich habe das Gefühl, dass dies sehr ähnlich aussehen wird wie dieconcurrentMap.compute
contstruct (z. B. siehediese Antwort) oder könnte ich diese Methode überhaupt anwenden?
map.compute(rowIndex, (key, value) -> {
if(value == null)
value = new Object();
synchronized (value) {
// access row
return value;
}
});
map.remove(rowIndex);
Ist der Wert und das "Synchronisieren" überhaupt notwendig, da wir bereits wissen, dass die Rechenoperation atomar ist?
// null is forbidden so use the key also as the value to avoid creating additional objects
ConcurrentHashMap<Integer, Integer> map = ...;
// now the row access looks really simple:
map.compute(rowIndex, (key, value) -> {
// access row
return key;
});
map.remove(rowIndex);
BTW: Seit wann haben wir diesen Rechner in Java. Seit 1.8? Kann dies nicht in den JavaDocs finden
Aktualisieren Ich habe eine sehr ähnliche Frage gefundenHieeachten Sie, dass die Frage mit userIds anstelle von rowIndices ein Beispiel mit mehreren Problemen enthält, z. B. fehlendes final
, @ anrufelock
innerhalb destry-finally
-Klausel und fehlende Verkleinerung der Karte. Auch scheint es @ zu geba Bibliothek JKeyLockManager zu diesem Zweck aberIch glaube nicht, dass es thread-safe ist.
Update 2: Die Lösung scheint wirklich einfach zu sein, als Nicolas Filotto darauf hinwies, wie das Entfernen vermieden werden kann:
map.compute(rowIndex, (key, value) -> {
// access row
return null;
});
So ist dies wirklich weniger speicherintensiv, ABER die einfache Segmentverriegelung mitsynchronized
ist in @ mindestens 50% schnell mein Szenario.