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.computecontstruct (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.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage