简体   繁体   English

java - 在 ScheduledThreadPoolExecutor 线程中的可见性

[英]java - visibility in ScheduledThreadPoolExecutor threads

I have a stop watch which runs for specified amount of time ( duration parameter) using inner single threaded scheduled executor:我有一个秒表,它使用内部单线程计划执行程序运行指定的时间量( 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);
    }

    (...)

}

My question is: will I run into any visibility problems with this approach (ie after executing .start() on StopWatch again once first cycle is finished)?我的问题是:这种方法是否会遇到任何可见性问题(即在第一个循环完成后再次在 StopWatch 上执行.start()之后)?

elapsedMillis and lastTimeMillis are updated in two threads and durationMillis is updated in first thread and read in second one, but it's happening rather sequentially and scheduled task starts after first thread is done with updating fields. elapsedMillislastTimeMillis在两个线程中更新,而durationMillis在第一个线程中更新并在第二个线程中读取,但它是按顺序发生的,计划任务在第一个线程完成更新字段后开始。 I'm still not certain if skipping volatility on those fields is safe though (probably not).我仍然不确定跳过这些领域的波动性是否安全(可能不安全)。

What you are doing can be better accomplished via the ScheduledExecutorService API:通过ScheduledExecutorService API 可以更好地完成你正在做的事情:

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() {

    }
}

Unless you really need to squeeze out the last bit of performance, I would not be worried about having a volatile more or less in the above code.除非你真的需要挤出最后一点性能,否则我不会担心上面代码中或多或少的 volatile。 So the simple answer to your question is: make the fields volatile.所以你的问题的简单答案是:让这些领域变得不稳定。

And there is a happens before relation between the schedule and the task being executed.时间表和正在执行的任务之间存在先于发生的关系。 See the memory consistency effects here:在此处查看 memory 一致性效果:

https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/concurrent/ExecutorService.html https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/concurrent/ExecutorService.html

So the task should be able to see the changes that were made before the task was placed on the executor.因此任务应该能够看到在任务被放置到执行器上之前所做的更改。 This is because the happens before relation is transitive.这是因为 happens before 关系是可传递的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM