java - 在 ScheduledThreadPoolExecutor 线程中的可见性
java - visibility in ScheduledThreadPoolExecutor threads
我有一个秒表,它使用内部单线程调度执行程序 运行s 指定时间量(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);
}
(...)
}
我的问题是:我是否会 运行 使用此方法解决任何可见性问题(即在第一个周期完成后再次在 StopWatch 上执行 .start()
之后)?
elapsedMillis
和 lastTimeMillis
在两个线程中更新,durationMillis
在第一个线程中更新并在第二个线程中读取,但它是按顺序发生的,计划任务在第一个线程之后开始完成更新字段。我仍然不确定跳过这些领域的波动性是否安全(可能不安全)。
通过 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() {
}
}
除非你真的需要榨取最后一点性能,否则我不会担心上面代码中或多或少的volatile。因此,您问题的简单答案是:使字段可变。
并且计划和正在执行的任务之间存在先行关系。在此处查看内存一致性效果:
所以任务应该能够看到在任务被放置到执行器之前所做的更改。这是因为 happens before 关系是可传递的。
我有一个秒表,它使用内部单线程调度执行程序 运行s 指定时间量(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);
}
(...)
}
我的问题是:我是否会 运行 使用此方法解决任何可见性问题(即在第一个周期完成后再次在 StopWatch 上执行 .start()
之后)?
elapsedMillis
和 lastTimeMillis
在两个线程中更新,durationMillis
在第一个线程中更新并在第二个线程中读取,但它是按顺序发生的,计划任务在第一个线程之后开始完成更新字段。我仍然不确定跳过这些领域的波动性是否安全(可能不安全)。
通过 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() {
}
}
除非你真的需要榨取最后一点性能,否则我不会担心上面代码中或多或少的volatile。因此,您问题的简单答案是:使字段可变。
并且计划和正在执行的任务之间存在先行关系。在此处查看内存一致性效果:
所以任务应该能够看到在任务被放置到执行器之前所做的更改。这是因为 happens before 关系是可传递的。