Java 固定线程池竞争条件?

Java fixed thread pool race condition?

考虑以下代码:

private static final Object LOCK = new Object();
private static final ExecutorService executorService = Executors.newFixedThreadPool(10); // Also used for a few other tasks.

public static void save(Object o) {
    String s = serialize(o);
    executorService.submit(() -> {
        // Do we have a race condition here?
        synchronized (LOCK) {
            saveToFile(s, new File("test.txt")); // overwrites the file
        }
    });
}

public static void main(String[] args) {
    save(args[0]);
    save(args[1]);
}

save(Object o) 仅在主线程上调用。我知道线程池按顺序处理内部队列中提交的任务,但理论上是否会发生,在达到 synchronized (LOCK) 之前存在竞争条件并且文件输出为 args[0]?

如果是,如何避免?我知道单线程执行器肯定能解决这个问题,但我想尽可能使用这个线程池。

编辑: 我认为一种可能的方法是使用 Queue:

private static final Queue<String> queue = new ConcurrentLinkedQueue<>();

public static void save(Object o) {
    queue.add(serialize(o));
    executorService.submit(() -> {
        synchronized (LOCK) {
            saveToFile(queue.remove(), new File("test.txt"));
        }
    });
}

在像您这样的生产者/消费者模式中,您通常通过 a blocking queue 在(多个)生产者和(单个)消费者之间转移任务。单一消费者保证了执行的顺序。

所以在伪代码中它会是这样的:

val queue = ArrayBlockingQueue() //could be another BlockingQueue
val executor = ...

main {
  executor.submit(consumer)
  queue.put(args[0])
  queue.put(args[1])
}

consumer() {
  try {
    while (true) {
      val file = queue.take() //blocks until a file is added to the queue
      save(file)
    }
  } catch (InterruptedException e) {
    Thread.currentThread(interrupt()) //restore the interrupt flag
    //do nothing, just exit
  }
}

如果你添加更多的消费者,你不能保证文件会被按顺序处理。但是您可以根据需要添加任意数量的生产者(即从不同的线程添加到队列)。