【问题标题】:java concurrency: lightweight nonblocking semaphore?java并发:轻量级非阻塞信号量?
【发布时间】:2011-03-22 17:13:21
【问题描述】:

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

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() method 没有返回值,并且不能使用 getCount() 原子地执行。

我应该只使用 Semaphore 还是有更合适的类?

【问题讨论】:

标签: java countdownlatch


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

    你可以使用类似的东西

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

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

    您可以再次使用 AtomicLong 来解决额外的偏执。

    【讨论】:

    • ...但是 getAndDecrement() 没有下限;我必须做很多小心的事情才能让它保持在零。
    • @Jason S,你为什么关心它是否低于零?如果您担心降频,可以使用 AtomicLong。
    • @Jason S,在这种情况下,答案是按照您的建议使用循环。无论如何,这就是 getAndDecrement() 的完成方式。
    • 是的,我同意,我(个人)对非阻塞的定义似乎比通常的定义要严格得多。
    • @jrudolph 我对“快速”和“巨大”的定义似乎与大多数人不同。我同意在足够低的规模上,CAS 上的循环是阻塞操作。这比大多数开发人员想象的要低得多。
    【解决方案3】:
    // 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 个线程的情况。如果您有更多,则需要添加其他一些退避或锁定机制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多