2016-03-15 9 views
9

Gibt es ein Szenario, in dem AtomicInteger.accumulateAndGet() nicht durch AtomicInteger.updateAndGet() ersetzt werden kann, oder ist es nur eine Annehmlichkeit für Methodenreferenzen?Gibt es einen funktionalen Unterschied zwischen AtomicInteger.updateAndGet() und AtomicInteger.accumulateAndGet()?

Hier ist ein einfaches Beispiel, wo ich sehe keinen funktionalen Unterschied:

AtomicInteger i = new AtomicInteger(); 
i.accumulateAndGet(5, Math::max); 
i.updateAndGet(x -> Math.max(x, 5)); 

Offensichtlich ist das gleiche gilt für getAndUpdate() und getAndAccumulate().

+2

Ist die Fähigkeit, vorhandene Funktionen (oder nicht erfassende Methodenreferenzen) zu verwenden, nicht hilfreich? Es gibt auch keinen funktionalen Unterschied zwischen "i.incrementAndGet()" und "i.accumulateAndGet (1, Integer :: sum)" ... – Holger

+0

@Holger 'accumulateAndGet()' wurde später hinzugefügt und kann nicht 'Unsafe' verwenden. 'addAndGet (1)' könnte ein besseres Beispiel sein, aber selbst das verwendete zum Zeitpunkt seiner Erstellung nicht 'Unsicher'. Ich akzeptiere jedoch Ihren allgemeinen Standpunkt; Diese Frage soll nur klären, ob das tatsächlich die Motivation war. – shmosel

Antwort

4

Im Zweifelsfall können Sie in implementation aussehen:

public final int accumulateAndGet(int x, 
            IntBinaryOperator accumulatorFunction) { 
    int prev, next; 
    do { 
     prev = get(); 
     next = accumulatorFunction.applyAsInt(prev, x); 
    } while (!compareAndSet(prev, next)); 
    return next; 
} 

public final int updateAndGet(IntUnaryOperator updateFunction) { 
    int prev, next; 
    do { 
     prev = get(); 
     next = updateFunction.applyAsInt(prev); 
    } while (!compareAndSet(prev, next)); 
    return next; 
} 

Sie unterscheiden sich nur in einzelnen Zeile und offensichtlich accumulateAndGet könnte leicht über updateAndGet ausgedrückt werden:

public final int accumulateAndGet(int x, 
            IntBinaryOperator accumulatorFunction) { 
    return updateAndGet(prev -> accumulatorFunction.applyAsInt(prev, x)); 
} 

So updateAndGet ist etwas Grund Operation und accumulateAndGet ist eine nützliche Abkürzung. Eine solche Verknüpfung kann besonders hilfreich sein, wenn Ihr x nicht effektiv ist endgültig:

int nextValue = 5; 
if(something) nextValue = 6; 
i.accumulateAndGet(nextValue, Math::max); 
// i.updateAndGet(prev -> Math.max(prev, nextValue)); -- will not work 
1

Es gibt Fälle, in denen eine Instanz Schöpfung durch die Verwendung accumulateAndGet vermieden werden können.

Dies ist nicht wirklich ein funktioneller Unterschied, aber es könnte nützlich sein, darüber zu wissen.

Betrachten Sie das folgende Beispiel:

void increment(int incValue, AtomicInteger i) { 
    // The lambda is closed over incValue. Because of this the created IntUnaryOperator 
    // will have a field which contains incValue. Because of this a new instance must 
    // be allocated on every call to the increment method. 
    i.updateAndGet(value -> incValue + value); 

    // The lambda is not closed over anything. The same IntBinaryOperator instance 
    // can be used on every call to the increment method, nothing need to be allocated. 
    i.accumulateAndGet(incValue, (incValueParam, value) -> incValueParam + value); 
} 

Instanz Kreationen können in der Regel nicht teuer sind, aber wichtig sein, von kurz Operationen, um loszuwerden, die sehr häufig in der Leistung sensiblen Orten verwendet werden.

Weitere Informationen darüber, wann Lambda-Instanzen wiederverwendet werden, finden Sie in this answer.