scheduledthreadpoolexecutor线程中的多线程java可见性

snz8szmq  于 2021-07-06  发布在  Java
关注(0)|答案(2)|浏览(407)

我有一个秒表,可以按规定的时间运行( duration 参数)使用内部单线程调度执行器:

public class StopWatch {

    private final AtomicBoolean running = new AtomicBoolean();
    private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);

    private long lastTimeMillis;
    private long durationMillis;
    private long elapsedMillis;
    private ScheduledFuture<?> future;

    public void start(long duration, TimeUnit timeUnit) {
        if (running.compareAndSet(false, true)) {
            durationMillis = timeUnit.toMillis(duration);
            lastTimeMillis = System.currentTimeMillis();
            elapsedMillis = 0;
            future = executor.schedule(this::tick, 0, TimeUnit.MILLISECONDS);
        }
    }

    (...)

    private void tick() {
        long now = System.currentTimeMillis();
        elapsedMillis += now - lastTimeMillis;
        lastTimeMillis = now;

        // do some stuff

        if (elapsedMillis < durationMillis) {
            future = executor.schedule(this::tick, 100, TimeUnit.MILLISECONDS);
            return;
        }

        running.compareAndSet(true, false);
    }

    (...)

}

我的问题是:我是否会遇到这种方法的可见性问题(即在执行之后) .start() 在第一个周期结束后再次使用秒表)? elapsedMillis 以及 lastTimeMillis 在两个线程和 durationMillis 在第一个线程中更新,在第二个线程中读取,但它是按顺序发生的,并且计划任务在第一个线程完成字段更新后开始。我仍然不确定跳过这些领域的波动是否安全(可能不安全)。

9q78igpj

9q78igpj1#

你正在做的事情可以通过 ScheduledExecutorService 应用程序编程接口:

public class StopWatch {
    private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    private ScheduledFuture<?> future;

    public void start(long duration, TimeUnit timeUnit) {
        if (future == null || future.isDone()) {
            future = executor.scheduleAtFixedRate(this::tick, 0L, 100L, TimeUnit.MILLISECONDS);
            executor.schedule((Runnable) () -> future.cancel(false), duration, timeUnit);
        }
    }

    private void tick() {

    }
}
mnemlml8

mnemlml82#

除非您真的需要挤出最后一点性能,否则我不会担心在上面的代码中有多少不稳定的代码。所以你的问题的简单答案是:让这些字段不稳定。
并且在计划和正在执行的任务之间有一个before关系。请在此处查看内存一致性效果:
https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/concurrent/executorservice.html
因此,任务应该能够看到在将任务放置到执行器之前所做的更改。这是因为before关系是可传递的。

相关问题