Search code examples
javacountdownlatch

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?


Solution

  • 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.