очередь LinkedList и потокобезопасность

#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 — это отличное решение другой проблемы. Я редактирую свой первоначальный вопрос, чтобы объяснить, что я блокирую ввод-вывод, а не очередь.