3

我有一个我想执行一次的回调的情况。为了争论,假设它看起来像这样:

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。否则返回假。这个操作对于多个线程来说是原子的。

我知道我可以为此使用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() 方法没有返回值,并且不能使用 getCount() 原子地执行。

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

4

3 回答 3

4

在单一许可证的情况下,您可以使用AtomicBoolean

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

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

于 2011-03-22T17:25:37.923 回答
1

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

你可以使用类似的东西

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

如果您不在递减和集合之间调用 20 亿次,这将起作用。也就是说,您需要在这两个语句之间停止 20 亿个线程。

同样,您可以使用 AtomicLong 来获得额外的偏执狂。

于 2011-03-22T17:18:15.713 回答
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 个线程的情况。如果您有更多,则需要添加其他一些退避或锁定机制。

于 2016-07-13T12:43:05.497 回答