首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java中的信号量和调度程序竞争条件

Java中的信号量和调度程序竞争条件
EN

Stack Overflow用户
提问于 2021-09-14 04:21:00
回答 1查看 76关注 0票数 0

我写了一个简单的速率限制器来限制远程服务的使用:

代码语言:javascript
复制
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

class SimpleRateLimiter {
    private Semaphore semaphore;
    private int maxPermits;
    private TimeUnit timePeriod;
    private ScheduledExecutorService scheduler;

    public static SimpleRateLimiter create(int permits, TimeUnit timePeriod) {
        SimpleRateLimiter limiter = new SimpleRateLimiter(permits, timePeriod);
        limiter.schedulePermitReplenishment();
        return limiter;
    }

    private SimpleRateLimiter(int permits, TimeUnit timePeriod) {
        this.semaphore = new Semaphore(permits);
        this.maxPermits = permits;
        this.timePeriod = timePeriod;
    }

    public boolean tryAcquire() {
        return semaphore.tryAcquire();
    }

    public void blockAcquire() throws InterruptedException {
        semaphore.acquire();
    }

    public void stop() {
        scheduler.shutdownNow();
        semaphore.drainPermits();
    }

    public int getPermitCount() {
        return semaphore.availablePermits();
    }

    public void schedulePermitReplenishment() {
        scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleWithFixedDelay(() -> {
            semaphore.release(maxPermits - semaphore.availablePermits());
        }, 0, 1, timePeriod);

    }
}

要使用它,我有以下条件:

代码语言:javascript
复制
SimpleRateLimiter rateLimiter = SimpleRateLimiter.create(100, TimeUnit.SECONDS);
...
//In some thread loop:
if (rateLimiter.tryAcquire()) {
    System.out.println("Permit left: " + rateLimiter.getPermitCount());
...
}

一切都很好,直到有一天它停止工作。检查日志,我发现rateLimiter.getPermitCount()达到104,(我怀疑)使maxPermits - semaphore.availablePermits()变为负值并抛出异常,导致schedulePermitReplenishment()内部的单线程调度器停止工作。我的问题是,考虑到信号量只能在内部访问且只能由单个线程访问,在什么情况下getPermitCount()可以超过100?

谢谢

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-09-14 04:33:17

你的代码有一些问题。但有一个问题是schedulePermitReplenishment方法中存在潜在的竞争条件。

如果并发调用此方法,例如,您将拥有9个可用的许可和10个最大许可,那么您可能会得到以下结果:

semaphore.release(10 - 9);

它与sempahore.release(1)相同。因此,如果有两个线程并发调用这个函数,那么最终会得到11个许可,而不是10个。

正如您已经指出的,下一次调用schedulePermitReplenishment时,您将以10-11=-1的许可结束,该许可将被释放,并且您将以异常(它确实被执行器吞噬)结束。

你为什么需要补充许可证?票丢了吗?因为这可能是可用票数大于最大票数的另一个来源。例如,如果取了一张票,你会调用replenish,而票最终会被退回,你最终也会得到太多的票。

我不太确定这段代码将如何使用,但您也可以在executor字段上进行数据竞争。也许在构造函数中创建executor而不是替换它会更好。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69171759

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档