java并发:轻量级非阻塞信号量?

时间:2011-03-22 17:13:21

标签: java countdownlatch

我有一种情况,我有一个我想要执行一次的回调。为了论证,让我们说它看起来像这样:

final X once = new X(1);
Runnable r = new Runnable() {
    @Override public void run() {
        if (once.use())
           doSomething();
    }
}

其中X是某个并发对象,具有以下行为:

  • 构造函数:X(int N) - 分配N次使用许可

  • boolean use():如果至少有1个使用许可,请使用其中一个并返回true。否则返回false。对于多个线程,此操作是原子操作。

我知道我可以使用java.util.concurrent.Semaphore,但我不需要它的阻塞/等待方面,我希望这是一次性使用。

除非我做

之类的事情,否则AtomicInteger看起来不够用
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;
       }
   }

我对while循环感到不安。

CountDownLatch将无效,因为countDown() method没有返回值,无法以原子方式执行w / r / t getCount()。

我应该只使用信号量还是有更合适的类?

3 个答案:

答案 0 :(得分:4)

如果是单一许可,您可以使用AtomicBoolean

final AtomicBoolean once = new AtomicBoolean(true);
Runnable r = new Runnable() {
    @Override public void run() {
        if (once.getAndSet(false))
           doSomething();
    }
}

如果您需要许多许可,请使用compareAndSet()的解决方案。不要担心循环,getAndIncrement()在封面下的工作方式相同。

答案 1 :(得分:1)

是肯定的。 AtomicInteger是非阻塞的。您可以使用getAndDecrement()。

您可以使用类似

的内容
if(counter.getAndDecrement() > 0) {
   // something
} else {
   counter.set(0);
}

如果您在减量和集合之间没有调用20亿次,这将有效。即你需要在这两个陈述之间停止20亿个线程。

你可以再次使用AtomicLong来获得额外的偏执狂。

答案 2 :(得分:0)

// 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;

如果您发现非法获得许可证,则将计数器设置回零会在另一个线程释放许可证时创建竞争条件。这仅适用于最多有2个或3个线程的情况。如果你有更多的话,还需要添加一些其他的退避或锁定机制。