我有一个秒表,可以按规定的时间运行( 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
在第一个线程中更新,在第二个线程中读取,但它是按顺序发生的,并且计划任务在第一个线程完成字段更新后开始。我仍然不确定跳过这些领域的波动是否安全(可能不安全)。
2条答案
按热度按时间9q78igpj1#
你正在做的事情可以通过
ScheduledExecutorService
应用程序编程接口:mnemlml82#
除非您真的需要挤出最后一点性能,否则我不会担心在上面的代码中有多少不稳定的代码。所以你的问题的简单答案是:让这些字段不稳定。
并且在计划和正在执行的任务之间有一个before关系。请在此处查看内存一致性效果:
https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/concurrent/executorservice.html
因此,任务应该能够看到在将任务放置到执行器之前所做的更改。这是因为before关系是可传递的。