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
}
}
如果你添加更多的消费者,你不能保证文件会被按顺序处理。但是您可以根据需要添加任意数量的生产者(即从不同的线程添加到队列)。
考虑以下代码:
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
}
}
如果你添加更多的消费者,你不能保证文件会被按顺序处理。但是您可以根据需要添加任意数量的生产者(即从不同的线程添加到队列)。