下面列出了java.util.concurrent.atomic.AtomicReferenceFieldUpdater#compareAndSet() 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
/**
* There are some scenarios where a completion {@link TerminalNotification} can be overridden with an error if
* errors are produced asynchronously.
* <p>
* This method helps set {@link TerminalNotification} atomically providing such an override.
*
* @param toSet {@link TerminalNotification} to set.
* @param overrideComplete Whether exisiting {@link TerminalNotification#complete()} should be overridden with the
* {@code toSet}.
* @param terminalNotificationUpdater {@link AtomicReferenceFieldUpdater} to access the current
* {@link TerminalNotification}.
* @param flagOwner instance of {@link R} that owns the current {@link TerminalNotification} field referenced by
* {@code terminalNotificationUpdater}.
* @param <R> Type of {@code flagOwner}.
* @return {@code true} if {@code toSet} is updated as the current {@link TerminalNotification}.
*/
public static <R> boolean trySetTerminal(TerminalNotification toSet, boolean overrideComplete,
AtomicReferenceFieldUpdater<R, TerminalNotification> terminalNotificationUpdater, R flagOwner) {
for (;;) {
TerminalNotification curr = terminalNotificationUpdater.get(flagOwner);
if (curr != null && !overrideComplete) {
// Once terminated, terminalNotification will never be set back to null.
return false;
} else if (curr == null && terminalNotificationUpdater.compareAndSet(flagOwner, null, toSet)) {
return true;
} else if (curr != null && curr.cause() == null) {
// Override complete
if (terminalNotificationUpdater.compareAndSet(flagOwner, curr, toSet)) {
return true;
}
} else {
return false;
}
}
}
/**
* A generic utility to atomically replace a subscription or cancel the replacement
* if current subscription is marked as cancelled (as in {@link #cancelledSubscription()})
* or was concurrently updated before.
* <p>
* The replaced subscription is itself cancelled.
*
* @param field The Atomic container
* @param instance the instance reference
* @param s the subscription
* @param <F> the instance type
*
* @return true if replaced
*/
public static <F> boolean set(AtomicReferenceFieldUpdater<F, Subscription> field,
F instance,
Subscription s) {
for (; ; ) {
Subscription a = field.get(instance);
if (a == CancelledSubscription.INSTANCE) {
s.cancel();
return false;
}
if (field.compareAndSet(instance, a, s)) {
if (a != null) {
a.cancel();
}
return true;
}
}
}
/**
* compareAndSet in one thread enables another waiting for value
* to succeed
*/
public void testCompareAndSetInMultipleThreads() throws Exception {
x = one;
final AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
a = updaterFor("x");
Thread t = new Thread(new CheckedRunnable() {
public void realRun() {
while (!a.compareAndSet(AtomicReferenceFieldUpdaterTest.this, two, three))
Thread.yield();
}});
t.start();
assertTrue(a.compareAndSet(this, one, two));
t.join(LONG_DELAY_MS);
assertFalse(t.isAlive());
assertSame(three, a.get(this));
}
/**
* Atomically set the field to a {@link Disposable} and dispose the old content.
*
* @param updater the target field updater
* @param holder the target instance holding the field
* @param newValue the new Disposable to set
* @return true if successful, false if the field contains the {@link #DISPOSED} instance.
*/
public static <T> boolean set(AtomicReferenceFieldUpdater<T, Disposable> updater, T holder, @Nullable Disposable newValue) {
for (;;) {
Disposable current = updater.get(holder);
if (current == DISPOSED) {
if (newValue != null) {
newValue.dispose();
}
return false;
}
if (updater.compareAndSet(holder, current, newValue)) {
if (current != null) {
current.dispose();
}
return true;
}
}
}
/**
* compareAndSet in one thread enables another waiting for value
* to succeed
*/
public void testCompareAndSetInMultipleThreads() throws Exception {
x = one;
final AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
a = updaterFor("x");
Thread t = new Thread(new CheckedRunnable() {
public void realRun() {
while (!a.compareAndSet(AtomicReferenceFieldUpdaterTest.this, two, three))
Thread.yield();
}});
t.start();
assertTrue(a.compareAndSet(this, one, two));
t.join(LONG_DELAY_MS);
assertFalse(t.isAlive());
assertSame(three, a.get(this));
}
@Test
@Tag("PASSING")
@Order(2)
public void compareAndSetUsingAtomicReferenceFieldUpdater() {
final AtomicReferenceFieldUpdater<TestSolutionCompareAndSet, Integer> valueUpdater =
AtomicReferenceFieldUpdater.newUpdater(TestSolutionCompareAndSet.class,
Integer.class,
"privateVolatile");
boolean exchanged = valueUpdater.compareAndSet(this, currentValue, newValue);
assertTrue(exchanged,
"The value should have been changed to 7, " +
"hence exchanged should be true"
);
assertEquals(newValue,
valueUpdater.get(this),
"The value of the privateVolatile should now be 7");
exchanged = valueUpdater.compareAndSet(this, 2, 33);
assertFalse(exchanged,
"The value should not have changed since the expected value " +
"did not match, hence exchanged should be false"
);
assertEquals(newValue,
valueUpdater.get(this),
"The value of the privateVolatile should still be 7");
}
@Test
@Tag("PASSING")
@Order(2)
public void compareAndSetUsingAtomicReferenceFieldUpdater() {
final AtomicReferenceFieldUpdater<TestKataCompareAndSet, Integer> valueUpdater =
AtomicReferenceFieldUpdater.newUpdater(TestKataCompareAndSet.class,
Integer.class,
"privateVolatile");
boolean exchanged = valueUpdater.compareAndSet(this, currentValue, newValue);
assertTrue(exchanged,
"The value should have been changed to 7, " +
"hence exchanged should be true"
);
assertEquals(newValue,
valueUpdater.get(this),
"The value of the privateVolatile should now be 7");
exchanged = valueUpdater.compareAndSet(this, 2, 33);
assertFalse(exchanged,
"The value should not have changed since the expected value " +
"did not match, hence exchanged should be false"
);
assertEquals(newValue,
valueUpdater.get(this),
"The value of the privateVolatile should still be 7");
}
@Nullable
@Override
public <T> T put(final Key<T> key, final T value,
CopyOnWriteAsyncContextMap owner,
AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
return mapUpdater.compareAndSet(owner, this, new OneAsyncContextMap(key, value)) ? null :
owner.put(key, value);
}
/**
* Update an empty atomic reference with the given exception, or combine further added
* exceptions together as suppressed exceptions under a root Throwable with
* the {@code "Multiple exceptions"} message, if the atomic reference already holds
* one. This is short-circuited if the reference contains {@link #TERMINATED}.
* <p>
* Since composite exceptions and traceback exceptions share the same underlying mechanism
* of suppressed exceptions, a traceback could be made part of a composite exception.
* Use {@link #unwrapMultipleExcludingTracebacks(Throwable)} to filter out such elements in
* a composite if needed.
*
* @param <T> the parent instance type
* @param field the target field updater
* @param instance the parent instance for the field
* @param exception the Throwable to add.
*
* @return true if added, false if the field contained the {@link #TERMINATED}
* instance.
* @see #unwrapMultiple(Throwable)
*/
public static <T> boolean addThrowable(AtomicReferenceFieldUpdater<T, Throwable> field,
T instance,
Throwable exception) {
for (; ; ) {
Throwable current = field.get(instance);
if (current == TERMINATED) {
return false;
}
if (current instanceof CompositeException) {
//this is ok, composite exceptions are never singletons
current.addSuppressed(exception);
return true;
}
Throwable update;
if (current == null) {
update = exception;
} else {
update = multiple(current, exception);
}
if (field.compareAndSet(instance, current, update)) {
return true;
}
}
}
static <T> boolean removeSequence(final T holder,
final AtomicReferenceFieldUpdater<T, Sequence[]> sequenceUpdater,
final Sequence sequence) {
int numToRemove;
Sequence[] oldSequences;
Sequence[] newSequences;
do {
oldSequences = sequenceUpdater.get(holder);
numToRemove = countMatching(oldSequences, sequence);
if (0 == numToRemove) {
break;
}
final int oldSize = oldSequences.length;
newSequences = new Sequence[oldSize - numToRemove];
for (int i = 0, pos = 0; i < oldSize; i++) {
final Sequence testSequence = oldSequences[i];
if (sequence != testSequence) {
newSequences[pos++] = testSequence;
}
}
} while (!sequenceUpdater.compareAndSet(holder, oldSequences, newSequences));
return numToRemove != 0;
}
/**
* Object arguments for parameters of type T that are not
* instances of the class passed to the newUpdater call will
* result in a ClassCastException being thrown.
*/
public void testFieldUpdaters_ClassCastException() {
// Use raw types to allow passing wrong object type, provoking CCE
final AtomicLongFieldUpdater longUpdater = aLongFieldUpdater();
final AtomicIntegerFieldUpdater intUpdater = anIntFieldUpdater();
final AtomicReferenceFieldUpdater refUpdater = anIntegerFieldUpdater();
final Object obj = new Object();
for (Object x : new Object[]{ new Object(), null }) {
Runnable[] throwingActions = {
() -> longUpdater.get(x),
() -> intUpdater.get(x),
() -> refUpdater.get(x),
() -> longUpdater.set(x, 17L),
() -> intUpdater.set(x, 17),
() -> refUpdater.set(x, (Integer) 17),
() -> longUpdater.addAndGet(x, 17L),
() -> intUpdater.addAndGet(x, 17),
() -> longUpdater.getAndUpdate(x, y -> y),
() -> intUpdater.getAndUpdate(x, y -> y),
() -> refUpdater.getAndUpdate(x, y -> y),
() -> longUpdater.compareAndSet(x, 17L, 42L),
() -> intUpdater.compareAndSet(x, 17, 42),
() -> refUpdater.compareAndSet(x, (Integer) 17, (Integer) 42),
};
assertThrows(ClassCastException.class, throwingActions);
}
}
private void mutateAdditionalResponseHeaders(
AtomicReferenceFieldUpdater<DefaultServiceRequestContext, HttpHeaders> atomicUpdater,
Consumer<HttpHeadersBuilder> mutator) {
for (;;) {
final HttpHeaders oldValue = atomicUpdater.get(this);
final HttpHeadersBuilder builder = oldValue.toBuilder();
mutator.accept(builder);
final HttpHeaders newValue = builder.build();
if (atomicUpdater.compareAndSet(this, oldValue, newValue)) {
return;
}
}
}
static private void testAtomicReferenceFieldUpdater() {
System.out.println("AtomicTest.testAtomicReferenceFieldUpdater:");
AtomicTest obj = new AtomicTest();
AtomicReferenceFieldUpdater<AtomicTest, Integer> updater = AtomicReferenceFieldUpdater.newUpdater(AtomicTest.class, Integer.class, "value");
System.out.println(updater.get(obj));
updater.set(obj, 5);
System.out.println(updater.get(obj));
updater.compareAndSet(obj, 4, -4);
System.out.println(updater.get(obj));
updater.compareAndSet(obj, 5, -5);
System.out.println(updater.get(obj));
}
/**
* Sets the given subscription once and returns true if successful, false
* if the field has a subscription already or has been cancelled.
* <p>
* If the field already has a subscription, it is cancelled and the duplicate
* subscription is reported (see {@link #reportSubscriptionSet()}).
*
* @param <F> the instance type containing the field
* @param field the field accessor
* @param instance the parent instance
* @param s the subscription to set once
* @return true if successful, false if the target was not empty or has been cancelled
*/
public static <F> boolean setOnce(AtomicReferenceFieldUpdater<F, Subscription> field, F instance, Subscription s) {
Objects.requireNonNull(s, "subscription");
Subscription a = field.get(instance);
if (a == CancelledSubscription.INSTANCE) {
s.cancel();
return false;
}
if (a != null) {
s.cancel();
reportSubscriptionSet();
return false;
}
if (field.compareAndSet(instance, null, s)) {
return true;
}
a = field.get(instance);
if (a == CancelledSubscription.INSTANCE) {
s.cancel();
return false;
}
s.cancel();
reportSubscriptionSet();
return false;
}
/**
* Atomically replace the {@link Disposable} in the field with the given new Disposable
* but do not dispose the old one.
*
* @param updater the target field updater
* @param holder the target instance holding the field
* @param newValue the new Disposable to set, null allowed
* @return true if the operation succeeded, false if the target field contained
* the common {@link #DISPOSED} instance and the given disposable is not null but is disposed.
*/
static <T> boolean replace(AtomicReferenceFieldUpdater<T, Disposable> updater, T holder, @Nullable Disposable newValue) {
for (;;) {
Disposable current = updater.get(holder);
if (current == DISPOSED) {
if (newValue != null) {
newValue.dispose();
}
return false;
}
if (updater.compareAndSet(holder, current, newValue)) {
return true;
}
}
}
/**
* Atomically set the field to the given non-null {@link Disposable} and return true,
* or return false if the field is non-null.
* If the target field contains the common {@link #DISPOSED} instance, the supplied disposable
* is disposed. If the field contains other non-null {@link Disposable}, an {@link IllegalStateException}
* is signalled to the {@code errorCallback}.
*
* @param updater the target field updater
* @param holder the target instance holding the field
* @param newValue the new Disposable to set, not null
* @return true if the operation succeeded, false
*/
public static <T> boolean setOnce(AtomicReferenceFieldUpdater<T, Disposable> updater, T holder, Disposable newValue,
Consumer<RuntimeException> errorCallback) {
Objects.requireNonNull(newValue, "newValue is null");
if (!updater.compareAndSet(holder, null, newValue)) {
newValue.dispose();
if (updater.get(holder) != DISPOSED) {
errorCallback.accept(new IllegalStateException("Disposable already pushed"));
}
return false;
}
return true;
}
/**
* Atomically try to set the given {@link Disposable} on the field if it is null or
* disposes it if the field contains {@link #DISPOSED}.
*
* @param updater the target field updater
* @param holder the target instance holding the field
* @param newValue the disposable to set
* @return true if successful, false otherwise
*/
public static <T> boolean trySet(AtomicReferenceFieldUpdater<T, Disposable> updater, T holder, Disposable newValue) {
if (!updater.compareAndSet(holder, null, newValue)) {
if (updater.get(holder) == DISPOSED) {
newValue.dispose();
}
return false;
}
return true;
}
static <T> void addSequence(final T holder,
final AtomicReferenceFieldUpdater<T, Sequence[]> updater,
final Sequence sequence) {
Sequence[] updatedSequences;
Sequence[] currentSequences;
do {
currentSequences = updater.get(holder);
updatedSequences = copyOf(currentSequences, currentSequences.length + 1);
updatedSequences[currentSequences.length] = sequence;
}
while (!updater.compareAndSet(holder, currentSequences, updatedSequences));
}
static <T> boolean removeSequence(final T holder,
final AtomicReferenceFieldUpdater<T, Sequence[]> sequenceUpdater,
final Sequence sequence) {
int numToRemove;
Sequence[] oldSequences;
Sequence[] newSequences;
do {
oldSequences = sequenceUpdater.get(holder);
numToRemove = countMatching(oldSequences, sequence);
if (0 == numToRemove) {
break;
}
final int oldSize = oldSequences.length;
newSequences = new Sequence[oldSize - numToRemove];
for (int i = 0, pos = 0; i < oldSize; i++) {
final Sequence testSequence = oldSequences[i];
if (sequence != testSequence) {
newSequences[pos++] = testSequence;
}
}
}
while (!sequenceUpdater.compareAndSet(holder, oldSequences, newSequences));
return numToRemove != 0;
}
/**
* A generic utility to atomically replace a subscription or cancel the replacement
* if the current subscription is marked as already cancelled (as in
* {@link #cancelledSubscription()}).
*
* @param field The Atomic container
* @param instance the instance reference
* @param s the subscription
* @param <F> the instance type
*
* @return true if replaced
*/
public static <F> boolean replace(AtomicReferenceFieldUpdater<F, Subscription> field,
F instance,
Subscription s) {
for (; ; ) {
Subscription a = field.get(instance);
if (a == CancelledSubscription.INSTANCE) {
s.cancel();
return false;
}
if (field.compareAndSet(instance, a, s)) {
return true;
}
}
}