在多线程环境中限制方法调用/秒

时间:2018-01-04 22:38:14

标签: java multithreading ratelimit

我正在开发用于处理游戏武器的机制。 我遇到了火灾的问题,它适用于单个线程,但似乎我的GUI从多个线程调用fire()方法,这有时会导致一些意外的行为(很快就会发射多个镜头)。 所以我在方法中添加了一个锁(带有AtomicBoolean标志),但它只会导致错误出现频率不足。

武器类

private final AtomicBoolean locked = new AtomicBoolean(false);
private final AtomicInteger fired = new AtomicInteger(0);

@Override
// returns true if a shot was fired; false otherwise
public boolean fire() {
    if (locked.compareAndSet(false, true)) {
        return false;
    }
    if (!canFire || reloading)
        return false;

    if (bulletsInMagazine > 0) {
        canFire = false;
        timeSinceLastShot = 0;
        bulletsInMagazine--;
        fired.incrementAndGet();
        if (bulletsInMagazine == 0) {
            reload();
        }
        locked.set(false);
        return true;
    }
    reload();
    locked.set(false);
    return false;
}

@Override
public void tick(int ms) {
    timeSinceLastShot += ms;
    if (timeSinceLastShot >= delayBetweenShotsMs) {
        canFire = true;
    }

    remainingReloadTime = Math.max(0, remainingReloadTime - ms);
    if (remainingReloadTime == 0 && reloading)
        processReloading();
}

我写了一个单元测试,有时会重现这个问题。

测试

@Test
public void testFiringRate() {
    int nbBullets = weapon.getBulletsInMagazine();
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(16);
    for (int i = 0; i < 16; i++) {
        scheduledExecutorService.scheduleAtFixedRate(weapon::fire, 5, 10, TimeUnit.MILLISECONDS);
    }
    System.out.println("START firing");
    int ticks = 0;
    while (ticks < 100) {
        weapon.tick(100);
        ticks++;
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int fired = nbBullets - weapon.getBulletsInMagazine();
        int reallyFired = weapon.getFiredShotCount();
        int diff = reallyFired - fired;
        if (diff != 0) {
            System.out.println(ticks + "\t" + fired + " shot(s)\t" + reallyFired + " really fired\t" + (diff != 0 ? "(+" + diff + ")" : ""));
        } else {
            System.out.print(".");
        }
    }
    System.out.println("\nSTOP firing");
    System.out.println(ticks + " Ticks");
    try {
        System.out.print("WAITING FOR ALL TASKS TO TERMINATE...");
        scheduledExecutorService.shutdown();
        scheduledExecutorService.awaitTermination(5, TimeUnit.SECONDS);
        System.out.println("DONE.");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    int bulletsAfter = weapon.getBulletsInMagazine();
    int fired = nbBullets - bulletsAfter;
    System.out.println("Shot fired: " + fired);
    int shotsFired = weapon.getFiredShotCount();
    System.out.println("Really fired: " + shotsFired);
    assertEquals(shotsFired, fired);
}

我得到的(有时): 计算出的镜头比现实少1或2(来自AtomicInteger 解雇

我想要的是什么: 无论线程数量多少,武器都应该尊重它的射速。

有什么想法吗?

修改

我试图反转if条件:

if (!locked.compareAndSet(false, true)) {
    return false;
}

它解决了单元测试的问题,但现在GUI只触发一次而且再也没有。

1 个答案:

答案 0 :(得分:0)

我按照@ichantz的建议将方法正文移动到synchronized(this)块中,并且它有效,因为经过多次尝试后我无法重现该错误。