#java #android #multithreading #collections
#java #Android #многопоточность #Коллекции
Вопрос:
У меня есть один поток, который add()
помещает объекты в очередь LinkedList, и другой поток, который poll()
устанавливает очередь для объектов для обработки. Это единственные два метода, которые я использую в своей очереди. Я никогда не выполняю итерацию по очереди, не добавляю и не удаляю объекты в середине списка. Я не могу придумать сценарий, когда два потока наступают друг на друга и каким-то образом повреждают данные, но, возможно, моего воображения просто не хватает.
Нажатие выполняется нечасто (несколько раз в секунду), но опрос выполняется очень часто (пару тысяч раз в секунду). Интересно, какой штраф я получу за синхронизацию add()
и poll()
. Это работает на Android.
Редактировать: я не ищу BlockingQueue
; Я блокирую ввод-вывод, а не объекты в очереди:
run()
Метод в опрашивающем потоке блокирует ожидание, пока в выходном буфере освободится место. Когда пространство становится доступным, он проверяет, есть ли у него какие-либо объекты, ожидающие в очереди. Если один из них доступен, он сериализует его в выходной буфер. Если очередь пуста (т.е. poll()
возвращает null
), это poll()
другие очереди с более низким приоритетом, и если все они пусты, сериализуется сообщение «сейчас данные недоступны».
Комментарии:
1. Имеет ли значение, каковы накладные расходы? У вас есть два потока, конкурирующих за доступ к одному и тому же ресурсу: вам нужна синхронизация. Как бы то ни было, обычно это не так уж плохо.
2. Зачем вы вообще проводите опрос? Можете ли вы вместо этого отправить «сообщение» непосредственно в другой поток (с новым объектом)? Проверьте циклы и обработчики для получения подробной информации о реализации
3. Спасибо за предложение по циклу. У меня мало опыта работы с библиотеками, специфичными для Android — благодарю вас за то, что обратили на это мое внимание. Я вижу, что это работает, вроде как. Принимающий поток не может обрабатывать объекты сразу по мере их получения — он блокируется при записи ввода-вывода. Чтобы это сработало, мне нужна локальная копия очереди в принимающем потоке. Если подумать, то я не уверен, что это вообще возможно — насколько я понимаю, циклические устройства блокируют свою очередь сообщений, и мне нужно заблокировать мой ввод-вывод.
Ответ №1:
Umn разве в Android нет BlockingQueues? Они предназначены именно для этого сценария, понятия не имею, почему вы хотите использовать что-то еще — не может быть намного эффективнее..
Комментарии:
1. Да, в Android есть
java.util.concurrent
и тому подобноеLinkedBlockingQueue
. Настоятельно рекомендуется для этого сценария.2. Спасибо за ваше предложение. Я вижу, что мой первоначальный вопрос неясен, что я блокирую ввод-вывод, а не доступность данных. Я редактирую вопрос, чтобы уточнить это.
3. @iter Ваш сценарий не мешает вам использовать BlockingQueues. Очевидно, что вы можете реализовать синхронизацию самостоятельно (блокирующие очереди в виде списков или массивов довольно тривиальны в реализации), но вы также можете использовать
poll()
метод, существующий как для реализации LinkedList, так и для Array. Или вы можете использоватьCollections.synchronizedList
и co — если бы мне пришлось угадывать, все три варианта (включая самореализацию) должны быть в основном идентичны с точки зрения производительности.
Ответ №2:
Без синхронизации может возникнуть ситуация, когда поток чтения опрашивает объект, который ТОЛЬКО что был добавлен в очередь, и что список не совсем завершил выход из метода add ().
Если вы заглянете в исходный код, то можете что-то напортачить с «expectedModCount = l.modCount;» потому что фиксация для опроса основана на строке под ним, прежде чем modCount будет фактически изменен.
По сути, ваше удаление происходит в идеальное время, вероятно, с 1 элементом, при одновременном добавлении другого и получении чего-то неправильного.
Чтобы предотвратить это, вы можете обернуть свои обращения в синхронизированные (lst) блоки {} или вы можете использовать параллельные классы. Я бы предпочел параллельные очереди, потому что poll не обязательно вращать — у вас может быть блокирующий take () .
Вы, вероятно, ищете: java.util.concurrent.ArrayBlockingQueue
ArrayBlockingQueue<String> que = new ArrayBlockingQueue<String>(100);
que.add("");
que.take();
Комментарии:
1. Благодарим вас за сценарий сбоя. Я добавляю синхронизацию в свой код. BlokingQueue — это отличное решение другой проблемы. Я редактирую свой первоначальный вопрос, чтобы объяснить, что я блокирую ввод-вывод, а не очередь.