Jason S Jason S - 4 months ago 18
Java Question

java concurrency: lightweight nonblocking semaphore?

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?

Answer

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.

Comments