Java固定线程池争用条件?
考虑以下代码:Java固定线程池争用条件?,java,multithreading,thread-safety,race-condition,synchronized,Java,Multithreading,Thread Safety,Race Condition,Synchronized,考虑以下代码: 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);
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(对象o)
仅在主线程上调用。我知道线程池会按顺序处理内部队列中提交的任务,但理论上会发生这种情况吗?在达到synchronized(LOCK)
之前会有竞争条件,并且文件输出是args[0]
如果是,如何避免这种情况?我知道单线程执行器肯定能解决这个问题,但如果可能的话,我想使用这个线程池
编辑:我认为一种可能的方法是使用队列
:
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"));
}
});
}
私有静态最终队列队列=新的ConcurrentLinkedQueue();
公共静态无效保存(对象o){
添加(序列化(o));
executorService.submit(()->{
已同步(锁定){
saveToFile(queue.remove(),新文件(“test.txt”);
}
});
}
在像您这样的生产者/消费者模式中,您通常通过在(多个)生产者和(单个)消费者之间传输任务。只有一个使用者这一事实确保了执行的顺序
所以在伪代码中,它应该是这样的:
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
}
}
如果添加更多使用者,则无法保证文件将按顺序处理。但是,您可以添加任意数量的生产者(即从不同线程添加到队列)。“在达到
同步(锁定)
之前存在竞争条件”-->是。“文件输出为args[0]
”-->并不总是这样,这取决于比赛的结果。“但是,你想避免什么呢?”布巴兰说。输出应该是args[1]
保证的。如果args[1]
是您想要的输出,为什么要调用args[0]
?或者您需要输出args[1]
,仅仅因为它是最后一个提交的?我想我找到了一种方法,但是我想知道是否有更好的解决方案。您可以创建一个锁、一个条件和一个公共计数器,每次,程序等待最后一个完成。我建议将方法的名称更改为consumer(),因为该方法正在使用而不是生成。很有趣,但这样做会在整个时间内“占用”池中的一个线程。不完全是我要找的。但是我喜欢这个主意。谢谢。你说“占据一条线”是什么意思?线程要么正在做一些工作,保存文件,要么将处于等待状态(不使用任何CPU资源)。我知道它在等待时不使用CPU。但是池中有固定数量的线程。那么,其中一个就不能再用于其他任务了。为什么不让使用者获取一个元素,而不是循环,并在元素放入队列的次数内提交使用者?这避免了阻塞线程,并且仍然保证按队列顺序写入。