如何释放Java Semaphore的所有许可

时间:2013-02-19 23:19:48

标签: java multithreading semaphore

所以这段代码:

int usedPermits = totalPermits - semaphore.availablePermits();
semaphore.release(usedPermits);

不是线程安全的,因为如果在两行之间另一个线程释放许可证,信号量的容量实际上将增加到其原始最大值之上。

这适用于我的情况,因为这段代码是1)单线程和2)唯一发布许可证的地方,这可能只是说明“全部释放”和“获取/释放”两个事实同一对象上的设计模式不兼容。

但是,我想询问是否存在具有不太精细的线程同步策略的首选模式。

4 个答案:

答案 0 :(得分:3)

正如其他答案中所解释的那样,有一种解决方案可以使您的代码成为原子。但是,在一般情况下,无法保证它会解决问题,因为这些代码在所有情况下都不正确。

使用它们的活动释放许可证,在这种情况下代码是多余的:信号量将自然补充,或者它们不会,在这种情况下你将需要那段代码,它将是安全的,只要你不做任何奇怪的事情。

请注意,您声明您希望限制每个时间段的活动率(此处为一分钟),但您必须考虑到活动的持续时间可能超过一分钟。你可以在这里限制两件事:

  • 每个量子开始的活动数量
  • 量子
  • 正在运行的活动数量

如果您希望限制第一个,那么您将需要您的代码来重新填写许可证,并让活动保留其许可证。如果你想 要处理第二种情况,那么你必须强制活动在分别开始和终止时获取和释放他们的许可。

如果您害怕某些活动滥用信号量,请禁止在活动代码中使用它。实际上,速率限制与活动语义完全正交,并且最好将该功能与活动主代码分开。因此,您应该使用代码来包装任何计划的活动来处理信号量:

class RateLimitedRunnable implements Runnable {
    Runnable runnable;
    RateLimitedRunnable(Runnable r) { runnable = r; }
    void Run() {
        semaphore.acquire();
        runnable.run();
        semaphore.release(); // remove if only limiting starts
    }
}

上面的示例(未经测试)代码描述了使用信号量远离实际活动的可能处理,从而消除了任何可能的误用。 如果内部活动需要访问信号量,那么它应该只是检索它的当前状态,并且肯定可以设计一个ad-hoc接口来提供有限的访问。

注意:我在这里使用“活动”一词作为线程或进程的意思,因为关于使用信号量的讨论比 Java 的上下文更通用。

答案 1 :(得分:0)

  

不是线程安全的,因为如果在两行之间另一个线程释放许可证,信号量的容量实际上将增加到其原始最大值以上。

这应该由synchronization解决,同时强制每个semaphore.release()都在synchronized块中,例如

synchronize(lock) {
    int usedPermits = totalPermits - semaphore.availablePermits();
    semaphore.release(usedPermits);
}

但是,如果您希望确保 超过totalPermits的许可数量,这可能还不够,因为即使{{1}也是如此} 释放所有块,如果一个名为synchronized的帖子在release之后超过semaphore

你可以实现一个totalPermits调用释放许可而不直接调用threads的函数,例如。

semaphore.release()

答案 2 :(得分:0)

如果它甚至可行的话,将某些东西粘在Semaphore上有点奇怪。这也会导致突发性 - 如果你每秒限制到10k QPS,那么你将重置并立即获得10000个查询。为什么不使用现有的RateLimiter实现?:

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/RateLimiter.html

至少可以看一下代码的灵感:

http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/util/concurrent/RateLimiter.java?name=v13.0-rc2

答案 3 :(得分:0)

为什么不扩展标准的Semaphore类并覆盖它的方法,但要使它们同步:

public class Semaphore extends java.util.concurrent.Semaphore {
public Semaphore(int permits)
{
    super(permits);
}

public synchronized void releaseAll()
{
    super.release(super.drainPermits());
}   
}