下面列出了java.util.concurrent.atomic.AtomicInteger#compareAndSet() 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Group("g4")
@GroupThreads(2)
@Benchmark
public void queueDrainAtomic4() {
AtomicInteger w = wip;
if (w.getAndIncrement() == 0) {
int missed = 1;
for (;;) {
counter++;
if (w.compareAndSet(missed, 0)) {
break;
}
missed = w.get();
}
}
}
@Benchmark
public void queueDrainAtomic4() {
AtomicInteger w = wip;
if (w.getAndIncrement() == 0) {
int missed = 1;
for (;;) {
counter++;
if (w.compareAndSet(missed, 0)) {
break;
}
missed = w.get();
}
}
}
/**
* Attempts to increment the given AtomicInteger without exceeding the
* specified maximum value. If the AtomicInteger cannot be incremented
* without exceeding the maximum, false is returned.
*
* @param counter
* The AtomicInteger to attempt to increment.
*
* @param max
* The maximum value that the given AtomicInteger should contain, or
* zero if no limit applies.
*
* @return
* true if the AtomicInteger was successfully incremented without
* exceeding the specified maximum, false if the AtomicInteger could
* not be incremented.
*/
private boolean tryIncrement(AtomicInteger counter, int max) {
// Repeatedly attempt to increment the given AtomicInteger until we
// explicitly succeed or explicitly fail
while (true) {
// Get current value
int count = counter.get();
// Bail out if the maximum has already been reached
if (count >= max && max != 0)
return false;
// Attempt to increment
if (counter.compareAndSet(count, count+1))
return true;
// Try again if unsuccessful
}
}
static boolean openQueryRequest(String project, int maxConcurrentQuery) {
if (maxConcurrentQuery == 0) {
return true;
}
try {
AtomicInteger nRunningQueries = runningStats.get(project);
for (;;) {
int nRunning = nRunningQueries.get();
if (nRunning < maxConcurrentQuery) {
if (nRunningQueries.compareAndSet(nRunning, nRunning + 1)) {
return true;
}
} else {
return false;
}
}
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
@AfterClass
public static void end() throws InterruptedException{
AtomicInteger mutex = new AtomicInteger(objectsToDelete.size());
for(ObjectToDelete o : objectsToDelete){
DBManager.deleteObject(o.getClassName(),o.getKey(),new DeleteCallback() {
@Override
public void done(ParseException arg0) {
synchronized (mutex) {
mutex.decrementAndGet();
}
}});
}
while (!mutex.compareAndSet(0, 0));
}
/**
* Attempts to increment the given AtomicInteger without exceeding the
* specified maximum value. If the AtomicInteger cannot be incremented
* without exceeding the maximum, false is returned.
*
* @param counter
* The AtomicInteger to attempt to increment.
*
* @param max
* The maximum value that the given AtomicInteger should contain, or
* zero if no limit applies.
*
* @return
* true if the AtomicInteger was successfully incremented without
* exceeding the specified maximum, false if the AtomicInteger could
* not be incremented.
*/
private boolean tryIncrement(AtomicInteger counter, int max) {
// Repeatedly attempt to increment the given AtomicInteger until we
// explicitly succeed or explicitly fail
while (true) {
// Get current value
int count = counter.get();
// Bail out if the maximum has already been reached
if (count >= max && max != 0)
return false;
// Attempt to increment
if (counter.compareAndSet(count, count+1))
return true;
// Try again if unsuccessful
}
}
/**
* Removes a number of occurrences of the specified element from this multiset. If the multiset
* contains fewer than this number of occurrences to begin with, all occurrences will be removed.
*
* @param element the element whose occurrences should be removed
* @param occurrences the number of occurrences of the element to remove
* @return the count of the element before the operation; possibly zero
* @throws IllegalArgumentException if {@code occurrences} is negative
*/
/*
* TODO(cpovirk): remove and removeExactly currently accept null inputs only
* if occurrences == 0. This satisfies both NullPointerTester and
* CollectionRemoveTester.testRemove_nullAllowed, but it's not clear that it's
* a good policy, especially because, in order for the test to pass, the
* parameter must be misleadingly annotated as @Nullable. I suspect that
* we'll want to remove @Nullable, add an eager checkNotNull, and loosen up
* testRemove_nullAllowed.
*/
@CanIgnoreReturnValue
@Override
public int remove(@Nullable Object element, int occurrences) {
if (occurrences == 0) {
return count(element);
}
CollectPreconditions.checkPositive(occurrences, "occurences");
AtomicInteger existingCounter = Maps.safeGet(countMap, element);
if (existingCounter == null) {
return 0;
}
while (true) {
int oldValue = existingCounter.get();
if (oldValue != 0) {
int newValue = Math.max(0, oldValue - occurrences);
if (existingCounter.compareAndSet(oldValue, newValue)) {
if (newValue == 0) {
// Just CASed to 0; remove the entry to clean up the map. If the removal fails,
// another thread has already replaced it with a new counter, which is fine.
countMap.remove(element, existingCounter);
}
return oldValue;
}
} else {
return 0;
}
}
}
private static int getNextId(final AtomicInteger id) {
while (true) {
int currentId = id.get();
int nextId = currentId + 1;
if (nextId == INVALID_ID) {
nextId++;
}
if (id.compareAndSet(currentId, nextId)) {
return currentId;
}
}
}
/**
* Atomically increments the given value by one, re-starting from 0 when the specified maximum is reached.
*
* @param value Value to increment.
* @param max Maximum after reaching which the value is reset to 0.
* @return Incremented value.
*/
private int incrementAndGet(AtomicInteger value, int max) {
while (true) {
int cur = value.get();
int next = cur == max ? 0 : cur + 1;
if (value.compareAndSet(cur, next))
return next;
}
}
public int updateAndGet(AtomicInteger control) {
int prev, next;
do {
prev = control.get();
next = prev == SIZE ? SIZE : prev + 1;
} while (!control.compareAndSet(prev, next));
return next;
}
/**
* Emits the given value if possible and terminates if there was an onComplete or onError
* while emitting, drops the value otherwise.
* @param <T> the value type
* @param observer the target Observer to emit to
* @param value the value to emit
* @param wip the serialization work-in-progress counter/indicator
* @param error the holder of Throwables
*/
public static <T> void onNext(Observer<? super T> observer, T value,
AtomicInteger wip, AtomicThrowable error) {
if (wip.get() == 0 && wip.compareAndSet(0, 1)) {
observer.onNext(value);
if (wip.decrementAndGet() != 0) {
Throwable ex = error.terminate();
if (ex != null) {
observer.onError(ex);
} else {
observer.onComplete();
}
}
}
}
/**
* Removes exactly the specified number of occurrences of {@code element}, or makes no
* change if this is not possible.
*
* <p>This method, in contrast to {@link #remove(Object, int)}, has no effect when the
* element count is smaller than {@code occurrences}.
*
* @param element the element to remove
* @param occurrences the number of occurrences of {@code element} to remove
* @return {@code true} if the removal was possible (including if {@code occurrences} is zero)
* @throws IllegalArgumentException if {@code occurrences} is negative
*/
@CanIgnoreReturnValue
public boolean removeExactly(@Nullable Object element, int occurrences) {
if (occurrences == 0) {
return true;
}
CollectPreconditions.checkPositive(occurrences, "occurences");
AtomicInteger existingCounter = Maps.safeGet(countMap, element);
if (existingCounter == null) {
return false;
}
while (true) {
int oldValue = existingCounter.get();
if (oldValue < occurrences) {
return false;
}
int newValue = oldValue - occurrences;
if (existingCounter.compareAndSet(oldValue, newValue)) {
if (newValue == 0) {
// Just CASed to 0; remove the entry to clean up the map. If the removal fails,
// another thread has already replaced it with a new counter, which is fine.
countMap.remove(element, existingCounter);
}
return true;
}
}
}
/**
* compareAndSet in one thread enables another waiting for value
* to succeed
*/
public void testCompareAndSetInMultipleThreads() throws Exception {
final AtomicInteger ai = new AtomicInteger(1);
Thread t = new Thread(new CheckedRunnable() {
public void realRun() {
while (!ai.compareAndSet(2, 3))
Thread.yield();
}});
t.start();
assertTrue(ai.compareAndSet(1, 2));
t.join(LONG_DELAY_MS);
assertFalse(t.isAlive());
assertEquals(3, ai.get());
}
public void testRandomOperationsWithSoftDeletes() throws Exception {
IndexWriterConfig iwc = newIndexWriterConfig();
AtomicInteger seqNo = new AtomicInteger(-1);
AtomicInteger retainingSeqNo = new AtomicInteger();
iwc.setSoftDeletesField("soft_deletes");
iwc.setMergePolicy(new SoftDeletesRetentionMergePolicy("soft_deletes",
() -> LongPoint.newRangeQuery("seq_no", retainingSeqNo.longValue(), Long.MAX_VALUE), newMergePolicy()));
try (Directory dir = newDirectory();
IndexWriter writer = new IndexWriter(dir, iwc);
SearcherManager sm = new SearcherManager(writer, new SearcherFactory())) {
Semaphore numOperations = new Semaphore(10 + random().nextInt(1000));
boolean singleDoc = random().nextBoolean();
Thread[] threads = new Thread[1 + random().nextInt(4)];
CountDownLatch latch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
latch.countDown();
try {
latch.await();
while (numOperations.tryAcquire()) {
String id = singleDoc ? "1" : Integer.toString(random().nextInt(10));
Document doc = new Document();
doc.add(new StringField("id", id, Field.Store.YES));
doc.add(new LongPoint("seq_no", seqNo.getAndIncrement()));
if (random().nextInt(10) <= 2) {
if (random().nextBoolean()) {
doc.add(new NumericDocValuesField(iwc.softDeletesField, 1));
}
writer.softUpdateDocument(new Term("id", id), doc, new NumericDocValuesField(iwc.softDeletesField, 1));
} else {
writer.addDocument(doc);
}
if (random().nextInt(100) < 10) {
int min = retainingSeqNo.get();
int max = seqNo.get();
if (min < max && random().nextBoolean()) {
retainingSeqNo.compareAndSet(min, min - random().nextInt(max - min));
}
}
if (random().nextInt(100) < 10) {
sm.maybeRefreshBlocking();
}
if (random().nextInt(100) < 5) {
writer.commit();
}
if (random().nextInt(100) < 1) {
writer.forceMerge(1 + random().nextInt(10), random().nextBoolean());
}
}
} catch (Exception e) {
throw new AssertionError(e);
}
});
threads[i].start();
}
for (Thread thread : threads) {
thread.join();
}
}
}
/**
* Sets the number of occurrences of {@code element} to {@code newCount}, but only if
* the count is currently {@code expectedOldCount}. If {@code element} does not appear
* in the multiset exactly {@code expectedOldCount} times, no changes will be made.
*
* @return {@code true} if the change was successful. This usually indicates
* that the multiset has been modified, but not always: in the case that
* {@code expectedOldCount == newCount}, the method will return {@code true} if
* the condition was met.
* @throws IllegalArgumentException if {@code expectedOldCount} or {@code newCount} is negative
*/
@CanIgnoreReturnValue
@Override
public boolean setCount(E element, int expectedOldCount, int newCount) {
checkNotNull(element);
checkNonnegative(expectedOldCount, "oldCount");
checkNonnegative(newCount, "newCount");
AtomicInteger existingCounter = Maps.safeGet(countMap, element);
if (existingCounter == null) {
if (expectedOldCount != 0) {
return false;
} else if (newCount == 0) {
return true;
} else {
// if our write lost the race, it must have lost to a nonzero value, so we can stop
return countMap.putIfAbsent(element, new AtomicInteger(newCount)) == null;
}
}
int oldValue = existingCounter.get();
if (oldValue == expectedOldCount) {
if (oldValue == 0) {
if (newCount == 0) {
// Just observed a 0; try to remove the entry to clean up the map
countMap.remove(element, existingCounter);
return true;
} else {
AtomicInteger newCounter = new AtomicInteger(newCount);
return (countMap.putIfAbsent(element, newCounter) == null)
|| countMap.replace(element, existingCounter, newCounter);
}
} else {
if (existingCounter.compareAndSet(oldValue, newCount)) {
if (newCount == 0) {
// Just CASed to 0; remove the entry to clean up the map. If the removal fails,
// another thread has already replaced it with a new counter, which is fine.
countMap.remove(element, existingCounter);
}
return true;
}
}
}
return false;
}
/**
* Sets the number of occurrences of {@code element} to {@code newCount}, but only if
* the count is currently {@code expectedOldCount}. If {@code element} does not appear
* in the multiset exactly {@code expectedOldCount} times, no changes will be made.
*
* @return {@code true} if the change was successful. This usually indicates
* that the multiset has been modified, but not always: in the case that
* {@code expectedOldCount == newCount}, the method will return {@code true} if
* the condition was met.
* @throws IllegalArgumentException if {@code expectedOldCount} or {@code newCount} is negative
*/
@CanIgnoreReturnValue
@Override
public boolean setCount(E element, int expectedOldCount, int newCount) {
checkNotNull(element);
checkNonnegative(expectedOldCount, "oldCount");
checkNonnegative(newCount, "newCount");
AtomicInteger existingCounter = Maps.safeGet(countMap, element);
if (existingCounter == null) {
if (expectedOldCount != 0) {
return false;
} else if (newCount == 0) {
return true;
} else {
// if our write lost the race, it must have lost to a nonzero value, so we can stop
return countMap.putIfAbsent(element, new AtomicInteger(newCount)) == null;
}
}
int oldValue = existingCounter.get();
if (oldValue == expectedOldCount) {
if (oldValue == 0) {
if (newCount == 0) {
// Just observed a 0; try to remove the entry to clean up the map
countMap.remove(element, existingCounter);
return true;
} else {
AtomicInteger newCounter = new AtomicInteger(newCount);
return (countMap.putIfAbsent(element, newCounter) == null)
|| countMap.replace(element, existingCounter, newCounter);
}
} else {
if (existingCounter.compareAndSet(oldValue, newCount)) {
if (newCount == 0) {
// Just CASed to 0; remove the entry to clean up the map. If the removal fails,
// another thread has already replaced it with a new counter, which is fine.
countMap.remove(element, existingCounter);
}
return true;
}
}
}
return false;
}
@Benchmark
public void wipCas() {
AtomicInteger v = value;
int w = v.get();
v.compareAndSet(w, w - 1);
}
/**
* Sets the number of occurrences of {@code element} to {@code newCount}, but only if
* the count is currently {@code expectedOldCount}. If {@code element} does not appear
* in the multiset exactly {@code expectedOldCount} times, no changes will be made.
*
* @return {@code true} if the change was successful. This usually indicates
* that the multiset has been modified, but not always: in the case that
* {@code expectedOldCount == newCount}, the method will return {@code true} if
* the condition was met.
* @throws IllegalArgumentException if {@code expectedOldCount} or {@code newCount} is negative
*/
@CanIgnoreReturnValue
@Override
public boolean setCount(E element, int expectedOldCount, int newCount) {
checkNotNull(element);
checkNonnegative(expectedOldCount, "oldCount");
checkNonnegative(newCount, "newCount");
AtomicInteger existingCounter = Maps.safeGet(countMap, element);
if (existingCounter == null) {
if (expectedOldCount != 0) {
return false;
} else if (newCount == 0) {
return true;
} else {
// if our write lost the race, it must have lost to a nonzero value, so we can stop
return countMap.putIfAbsent(element, new AtomicInteger(newCount)) == null;
}
}
int oldValue = existingCounter.get();
if (oldValue == expectedOldCount) {
if (oldValue == 0) {
if (newCount == 0) {
// Just observed a 0; try to remove the entry to clean up the map
countMap.remove(element, existingCounter);
return true;
} else {
AtomicInteger newCounter = new AtomicInteger(newCount);
return (countMap.putIfAbsent(element, newCounter) == null)
|| countMap.replace(element, existingCounter, newCounter);
}
} else {
if (existingCounter.compareAndSet(oldValue, newCount)) {
if (newCount == 0) {
// Just CASed to 0; remove the entry to clean up the map. If the removal fails,
// another thread has already replaced it with a new counter, which is fine.
countMap.remove(element, existingCounter);
}
return true;
}
}
}
return false;
}
/**
* Adds a number of occurrences of the specified element to this multiset.
*
* @param element the element to add
* @param occurrences the number of occurrences to add
* @return the previous count of the element before the operation; possibly zero
* @throws IllegalArgumentException if {@code occurrences} is negative, or if
* the resulting amount would exceed {@link Integer#MAX_VALUE}
*/
@CanIgnoreReturnValue
@Override
public int add(E element, int occurrences) {
checkNotNull(element);
if (occurrences == 0) {
return count(element);
}
CollectPreconditions.checkPositive(occurrences, "occurences");
while (true) {
AtomicInteger existingCounter = Maps.safeGet(countMap, element);
if (existingCounter == null) {
existingCounter = countMap.putIfAbsent(element, new AtomicInteger(occurrences));
if (existingCounter == null) {
return 0;
}
// existingCounter != null: fall through to operate against the existing AtomicInteger
}
while (true) {
int oldValue = existingCounter.get();
if (oldValue != 0) {
try {
int newValue = IntMath.checkedAdd(oldValue, occurrences);
if (existingCounter.compareAndSet(oldValue, newValue)) {
// newValue can't == 0, so no need to check & remove
return oldValue;
}
} catch (ArithmeticException overflow) {
throw new IllegalArgumentException("Overflow adding " + occurrences + " occurrences to a count of " + oldValue);
}
} else {
// In the case of a concurrent remove, we might observe a zero value, which means another
// thread is about to remove (element, existingCounter) from the map. Rather than wait,
// we can just do that work here.
AtomicInteger newCounter = new AtomicInteger(occurrences);
if ((countMap.putIfAbsent(element, newCounter) == null)
|| countMap.replace(element, existingCounter, newCounter)) {
return 0;
}
break;
}
}
// If we're still here, there was a race, so just try again.
}
}
/**
* Adds a number of occurrences of the specified element to this multiset.
*
* @param element the element to add
* @param occurrences the number of occurrences to add
* @return the previous count of the element before the operation; possibly zero
* @throws IllegalArgumentException if {@code occurrences} is negative, or if
* the resulting amount would exceed {@link Integer#MAX_VALUE}
*/
@CanIgnoreReturnValue
@Override
public int add(E element, int occurrences) {
checkNotNull(element);
if (occurrences == 0) {
return count(element);
}
CollectPreconditions.checkPositive(occurrences, "occurences");
while (true) {
AtomicInteger existingCounter = Maps.safeGet(countMap, element);
if (existingCounter == null) {
existingCounter = countMap.putIfAbsent(element, new AtomicInteger(occurrences));
if (existingCounter == null) {
return 0;
}
// existingCounter != null: fall through to operate against the existing AtomicInteger
}
while (true) {
int oldValue = existingCounter.get();
if (oldValue != 0) {
try {
int newValue = IntMath.checkedAdd(oldValue, occurrences);
if (existingCounter.compareAndSet(oldValue, newValue)) {
// newValue can't == 0, so no need to check & remove
return oldValue;
}
} catch (ArithmeticException overflow) {
throw new IllegalArgumentException(
"Overflow adding " + occurrences + " occurrences to a count of " + oldValue);
}
} else {
// In the case of a concurrent remove, we might observe a zero value, which means another
// thread is about to remove (element, existingCounter) from the map. Rather than wait,
// we can just do that work here.
AtomicInteger newCounter = new AtomicInteger(occurrences);
if ((countMap.putIfAbsent(element, newCounter) == null)
|| countMap.replace(element, existingCounter, newCounter)) {
return 0;
}
break;
}
}
// If we're still here, there was a race, so just try again.
}
}