【问题标题】:java - visibility in ScheduledThreadPoolExecutor threadsjava - ScheduledThreadPoolExecutor 线程中的可见性
【发布时间】:2020-11-15 15:35:18
【问题描述】:

我有一个秒表,它使用内部单线程调度执行器运行指定的时间量(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() 之后)?

elapsedMillislastTimeMillis 在两个线程中更新,durationMillis 在第一个线程中更新并在第二个线程中读取,但它是按顺序发生的,并且计划任务在第一个线程完成更新字段后开始。我仍然不确定跳过这些领域的波动是否安全(可能不是)。

【问题讨论】:

    标签: java multithreading thread-safety volatility


    【解决方案1】:

    除非你真的需要挤出最后一点性能,否则我不会担心在上面的代码中或多或少有一个 volatile。因此,您的问题的简单答案是:使字段可变。

    在计划和正在执行的任务之间有一个发生之前的关系。在此处查看内存一致性效果:

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

    因此,该任务应该能够看到在将任务置于执行程序之前所做的更改。这是因为发生在关系之前是传递的。

    【讨论】:

      【解决方案2】:

      您正在做的事情可以通过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() {
      
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-08
        • 2016-12-08
        • 1970-01-01
        • 1970-01-01
        • 2017-03-18
        • 2011-02-16
        相关资源
        最近更新 更多