Параллельная и блокирующая очередь в Java
у меня есть классическая проблема потока, толкающего события во входящую очередь второго потока. Только на этот раз мне очень интересно выступление. Чего я хочу добиться, так это:
- я хочу параллельный доступ к очереди, производитель толкает, приемник выскакивает.
- когда очередь пуста, я хочу, чтобы потребитель блокировал очередь, ожидая производителя.
моей первой идеей было использовать LinkedBlockingQueue
, но я скоро понял, что это не совпадение, и производительность пострадала. С другой стороны, теперь я использую ConcurrentLinkedQueue
, но все же я плачу стоимость wait()
/ notify()
на каждой публикации. Поскольку потребитель, найдя пустую очередь, не блокирует, я должен синхронизировать и wait()
на замок. С другой стороны, производитель должен получить этот замок и notify()
после каждой публикации. Общий результат заключается в том, что я плачу стоимость
sycnhronized (lock) {lock.notify()}
в каждой публикации, даже когда не необходимый.
то, что я думаю, необходимо здесь, это очередь, которая блокирует и одновременно. Я представляю себе push()
операции для работы как в ConcurrentLinkedQueue
, с дополнительным notify()
к объекту, когда выталкиваемый элемент является первым в списке. Такую проверку я считаю уже существующей в ConcurrentLinkedQueue
, так как нажатие требует подключения к следующему элементу. Таким образом, это будет намного быстрее, чем синхронизация каждый раз на внешнем замке.
что-то вроде этого доступный / разумный?
6 ответов
Я думаю, вы можете придерживаться java.util.concurrent.LinkedBlockingQueue
независимо от ваших сомнений. Это совпадение. Хотя, я понятия не имею о его исполнении. Наверное, другая реализация BlockingQueue
лучше вам подойдет. Их не так много, поэтому сделайте тесты производительности и измерьте.
похоже на этот ответ https://stackoverflow.com/a/1212515/1102730 но немного по-другому.. В итоге я использовал ExecutorService
. Вы можете создать экземпляр с помощью Executors.newSingleThreadExecutor()
. Мне нужна параллельная очередь для чтения / записи BufferedImages в файлы, а также атомарность с чтением и записью. Мне нужен только один поток, потому что файл IO на порядок быстрее, чем исходный, чистый IO. Кроме того, меня больше волновала атомарность действий и правильность, чем производительность, но этот подход также можно сделать с несколькими потоками в пуле, чтобы ускорить работу.
чтобы получить изображение (Try-Catch-Finally опущено):
Future<BufferedImage> futureImage = executorService.submit(new Callable<BufferedImage>() {
@Override
public BufferedImage call() throws Exception {
ImageInputStream is = new FileImageInputStream(file);
return ImageIO.read(is);
}
})
image = futureImage.get();
чтобы сохранить изображение (Try-Catch-окончательно опущено):
Future<Boolean> futureWrite = executorService.submit(new Callable<Boolean>() {
@Override
public Boolean call() {
FileOutputStream os = new FileOutputStream(file);
return ImageIO.write(image, getFileFormat(), os);
}
});
boolean wasWritten = futureWrite.get();
важно отметить, что вы должны смыть и закрыть свои потоки в блоке finally. Я не знаю, как он работает по сравнению с другими решениями, но он довольно универсален.
Я бы предложил вам посмотреть на ThreadPoolExecutor newSingleThreadExecutor. Он будет обрабатывать ваши задачи, заказанные для вас, и если вы отправите Callables вашему исполнителю вы также сможете получить блокирующее поведение, которое вы ищете.
вы можете попробовать LinkedTransferQueue из jsr166:http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/
оно выполняет ваши требования и имеет меньше накладных расходов для деятельности предложения/опроса. Как я вижу из кода, когда очередь не пуста, она использует атомарные операции для элементов опроса. И когда очередь пуста, она некоторое время вращается и паркует поток в случае неудачи. Я думаю, это может помочь в вашем случае.
Я использую ArrayBlockingQueue всякий раз, когда мне нужно передать данные из одного потока в другой. Используя методы put и take (которые будут блокировать, если full/empty).
здесь список классов, реализующих BlockingQueue
.
Я бы рекомендовал проверить SynchronousQueue
.
как и @Rorick, упомянутый в его комментарии, я считаю, что все эти реализации параллельны. Я думаю, что ваши проблемы с LinkedBlockingQueue
может быть не на месте.