Closed. This question needs to be more focused。它当前不接受答案。












想改善这个问题吗?更新问题,使其仅关注editing this post一个问题。

4个月前关闭。



Improve this question




我有以下代码(嵌套类):
public final class Energy {

    private final int defHours = plugin.getConfiguration().defaultEnergy;
    private final int resHours = plugin.getConfiguration().restoreEnergy;
    private final int maxHours = plugin.getConfiguration().energyCapacity;

    private BukkitTask energyTask;
    private final Runnable energyCheck = () -> {
        plugin.getLogger().info(id + " TASK Energy " + this.energy + ", consumption " + this.consumption);
        if (this.energy == 0) {
            plugin.getLogger().info(id + " TASK Energy " + this.energy + ", consumption " + this.consumption);
            if (this.consumption != 0) unclaimAll(); // LINE 1178
            return;
        }
        if ((this.energy -= this.consumption) <= 0) {
            this.energy = 0;
            unclaimAll();
            energyTask.cancel();
            energyTask = null;
        }
        update();
    };

    private int energy;
    private int consumption = -1; //per minute

    private Energy(final int energy) {
        this.energy = energy;
    }

    public final void start() throws IllegalStateException {
        if (isAdminGuild()) return;
        plugin.getLogger().info("ENERGY START " + Guild.this.id + " " + Guild.this.name);
        if (this.consumption != -1) throw new IllegalStateException("Already started");
        this.consumption = members.size() * claims.size();
        plugin.getLogger().info("Energy " + this.energy + ", consumption " + this.consumption);
        if (consumption != 0) this.energyTask = scheduler.runTaskTimer(plugin, this.energyCheck, 100, 1200);
    }

    public final void update() {
        if (isAdminGuild()) return;
        if (this.consumption == -1) throw new IllegalStateException("Not started");
        final boolean noConsumption = (this.consumption = members.size() * claims.size()) == 0;
        final boolean hasEnergy = this.energy > 0;
        if (noConsumption && hasEnergy) {
            if (this.energyTask != null) this.energyTask.cancel();
            this.energyTask = null;
            this.energy = 0;
        }
        else if (!noConsumption && !hasEnergy) {
            this.energy = this.consumption * 60 * (this.energy == -1 ? this.defHours : this.resHours);
            this.energyTask = scheduler.runTaskTimer(plugin, this.energyCheck, 1, 1200);
        }
        plugin.getLogger().info(id + " UPDATE Energy " + this.energy + ", consumption " + this.consumption);
        if (getHours() > this.maxHours) setHours(this.maxHours);
        onlineMembers.forEach(p -> {
            if (p != null && p.scoreboard != null) p.scoreboard.updateEnergyScore();
        });
    }

    public final int getEnergy() {
        return this.energy;
    }

    public final void addEnergy(final int energy) {
        if (energy < 0) throw new IllegalArgumentException("Energy cannot be negative");
        this.energy += energy;
        update();
    }

    public final int getMaxEnergy() {
        return isAdminGuild() || this.consumption <= 0 ? 0 : this.consumption * 60 * this.maxHours;
    }

    public final int getConsumption() {
        return this.consumption;
    }

    public final int getMinutes() {
        return this.consumption <= 0 ? 0 : this.energy / this.consumption;
    }

    public final void setMinutes(final int minutes) {
        if (minutes == 0) unclaimAll();
        this.energy = minutes * this.consumption;
        update();
    }

}

如您所见,我在那里有一个energyCheck Runnable,它每分钟在主线程(称为线程A)上运行。它检查energy值是否等于零,如果是,则调用unclaimAll()

现在发生了一件非常奇怪的事情。我添加了一些调试日志。我有一个此类的特定实例,该实例由线程B创建/初始化。此后几行,线程B还调用start()方法。初始化时,根据调用energy方法时所打印的日志,为32615赋予值start()consumption的值为16。

几秒钟后,我看到任务正在运行,第一行调试显示energy为32615,而consumption为16。但是,在下一秒,unclaimAll()方法正在运行,而第二调试行则未显示。控制台显示以下内容:

[11:08:26] [服务器线程/信息]:[xGuilds] 680任务能源25367,消耗16否
[11:08:26] [服务器线程/ INFO]:[xGuilds] 680 UPDATE能量25351,消耗16

我在unclaimAll()方法中打印RuntimeException,以查看该方法的调用位置,它指向第1178行:
java.lang.RuntimeException
  at eu.taigacraft.xguilds.guild.ChunkLoc.cooldown(ChunkLoc.java:43) ~[?:?]
  at eu.taigacraft.xguilds.guild.ChunkLoc.unclaim(ChunkLoc.java:66) ~[?:?]
  at java.lang.Iterable.forEach(Iterable.java:75) ~[?:1.8.0_232]
  at eu.taigacraft.xguilds.guild.Guild.unclaimAll(Guild.java:574) ~[?:?]
  at eu.taigacraft.xguilds.guild.Guild$Energy.lambda$new$0(Guild.java:1178) ~[?:?]
  ...

更新:我在第574行之前在unclaimAll()方法中放置了一条日志消息,但该消息没有发送...这怎么可能?

奇怪的是,仅当energy为0时才调用第1178行,而之前的调试日志明确指出它是32615。这怎么可能发生?我唯一想到的就是并发修改,但是正如我所说的,energyCheck任务仅在主线程(线程A)上运行。 update()方法是energy可以设置为0的唯一位置,除了energyTask之外,只有极少数其他情况可以调用update(),这在此特定时刻极不可能发生。最奇怪的是,下次运行energyCheck任务时,调试显示energy为32599,这恰好是初始值减去消耗(32615-16)。在此任务的第二次运行中,就像此后的其他所有运行一样,没有调用unclaimAll() ...

我不知道这是怎么发生的,我也不知道如何重现它。它也仅在Energy的一些特定实例中发生,并且每次都是相同的实例...任何帮助表示赞赏。

最佳答案

正如许多人在评论中所说,您没有为我们提供足够的上下文来重现此问题。

但是,对我来说很清楚,您看到的是某种竞争条件,其中在多个线程上读取或写入变量的值。

我建议您尝试将energyconsumptionint更改为AtomicInteger,看看是否可以使您获得更一致的行为。

关于java - 奇怪的整数行为/部分执行方法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/61499899/

10-09 13:45