I have a situation where I have a callback that I want to execute once. For the sake of argument let's say it looks like this:
final X once = new X(1);
Runnable r = new Runnable() {
@Override public void run() {
if (once.use())
doSomething();
}
}
where X is some concurrent object with the following behavior:
constructor: X(int N) -- allocates N use permits
boolean use()
: If there is at least 1 use permit, consume one of them and return true. Otherwise return false. This operation is atomic with respect to multiple threads.
I know I can use java.util.concurrent.Semaphore for this, but I don't need the blocking/waiting aspect of it, and I want this to be a one-time use thing.
AtomicInteger doesn't look sufficient unless I do something like
class NTimeUse {
final private AtomicInteger count;
public NTimeUse(int N) { this.count = new AtomicInteger(N); }
public boolean use() {
while (true)
{
int n = this.count.get();
if (n == 0)
return false;
if (this.count.compareAndSet(n, n-1))
return true;
}
}
and I feel queasy about the while loop.
开发者_运维问答CountDownLatch won't work, because the countDown() method has no return value and can't be executed atomically w/r/t getCount().
Should I just use Semaphore or is there a more appropriate class?
In the case of single permit you can use AtomicBoolean
:
final AtomicBoolean once = new AtomicBoolean(true);
Runnable r = new Runnable() {
@Override public void run() {
if (once.getAndSet(false))
doSomething();
}
}
If you need many permits, use your solution with compareAndSet()
. Don't worry about the loop, getAndIncrement()
works the same way under the cover.
yes. AtomicInteger is non-blocking. You can use getAndDecrement().
You can use something like
if(counter.getAndDecrement() > 0) {
// something
} else {
counter.set(0);
}
This will work provided you don't call it two billion times between the decrement and the set. i.e. you would need to have two billion threads stop between these two statements.
Again you can use AtomicLong for extra paranoia.
// This implements an unfair locking scheme:
while ( mayContinue() ) {
// acquire the permit and check if it was legally obtained
if ( counter.decrementAndGet() > 0 )
return true;
// return the illegally acquired permit
counter.incrementAndGet();
}
return false;
Setting the counter back to zero if you discover the permit was illegally obtained creates a race condition when another thread releases a permit. This only works for situations where there are 2 or 3 threads at most. Some other backoff or latching mechanism needs to be added if you have more.
精彩评论