2 Stimmen

Java-Synchronisierung auf Collection mit teuren Operationen

Ich habe eine Liste namens synchronizedMap, auf die ich in meiner Funktion doMapOperation synchronisiere. In dieser Funktion muss ich Elemente zu einer Map hinzufügen/entfernen und auf diesen Objekten aufwendige Operationen ausführen. Ich weiß, dass ich keine teure Operation in einem synchronisierten Block aufrufen möchte, aber ich weiß nicht, wie ich sicherstellen kann, dass die Map konsistent ist, während ich diese Operationen durchführe. Wie mache ich das am besten?

Das ist mein initiales Layout, von dem ich sicher bin, dass es falsch ist, weil man teure Operationen in einem synchronisierten Block vermeiden möchte:

public void doMapOperation(Object key1, Object key2) {
    synchronized (synchronizedMap) {

        // Entfernen von key1, wenn es vorhanden ist.
        if (synchronizedMap.containsKey(key1)) {
            Object value = synchronizedMap.get(key1);
            value.doExpensiveOperation(); // Sollte nicht im synchronisierten Block sein.

            synchronizedMap.remove(key1);
        }

        // Hinzufügen von key2, wenn nötig.
        Object value = synchronizedMap.get(key2);
        if (value == null) {
            Object value = new Object();
            synchronizedMap.put(key2, value);
        }

        value.doOtherExpensiveOperation(); // Sollte nicht im synchronisierten Block sein.
    } // Ende der Synchronisation.
}

Als Fortführung dieser Frage: Wie würden Sie dies in einer Schleife tun?

public void doMapOperation(Object... keys) {
    synchronized (synchronizedMap) {

        // Durchlaufen der Keys und Entfernen von ihnen.
        for (Object key : keys) {
            // Überprüfen, ob die Map den Key hat, entfernen, wenn der Key existiert, hinzufügen, wenn der Key nicht existiert.
            if (synchronizedMap.containsKey(key)) {
                Object value = synchronizedMap.get(key);
                value.doExpensiveOperation(); // Sollte hier nicht sein.

                synchronizedMap.remove(key);
            } else {
                Object value = new Object();
                value.doAnotherExpensiveOperation(); // Sollte nicht hier sein.

                synchronizedMap.put(key, value);
            }
        }
    } // Ende des Synchronisationsblocks.
}

Vielen Dank für die Hilfe.

1voto

user207421 Punkte 297318

Wenn Sie keine Nullwerte in der Map haben, benötigen Sie den Aufruf von containsKey() überhaupt nicht: Sie können Map.remove() verwenden, um das Element sowohl zu entfernen als auch Ihnen mitzuteilen, ob es vorhanden war. Der wahre Inhalt Ihres synchronisierten Blocks muss also nur folgendes sein:

Object value = Map.remove(key);
if (value != null)
  value.doExpensiveOperation();
else
{
  value = new Value();
  value.doExpensiveOperation();
  map.put(key,value);
}

Wenn die teure Operation selbst nicht synchronisiert werden muss, d.h. wenn es Ihnen nichts ausmacht, dass andere Clients der Map den Wert sehen, während er bearbeitet wird, können Sie weiter vereinfachen zu:

Object value = Map.remove(key);
if (value == null)
{
  value = new Value();
  map.put(key,value);
}
value.doExpensiveOperation();

und der synchronisierte Block kann vor der teuren Operation beendet werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X