В общем, это очень плохая идея, чтобы работать одновременно на структура данных, не поточно- , У вас нет гарантии, что реализация не изменится в будущем, что может серьезно повлиять на поведение среды выполнения приложения, то есть java.util.HashMap вызывает бесконечные циклы при одновременном изменении.
Для доступа к списку одновременно Java предоставляет java.util.concurrent.CopyOnWriteArrayList
.С помощью этой реализации будет решить вашу проблему различных способов:
- это потокобезопасно, что позволяет одновременно модификации
- итерации снимки списка не зависят от параллельных операций оных, что позволяет одновременно добавлять и перебор
- это быстрее, чем синхронизация
в качестве альтернативы, если не используя копию внутреннего массива является строгое требование (что я не могу себе представить в вашем случае, массив довольно мал, поскольку он содержит только ссылки на объекты, которые можно скопировать в памяти довольно эффективно), вы можете синхронизировать доступ на карте. Но для этого требуется, чтобы карта была правильно инициализирована, в противном случае ваш код может вызывать NullPointerException
, потому что порядок выполнения потока не гарантируется (вы предполагаете, что populateList()
запущен раньше, поэтому список инициализируется. При использовании синхронизированного блока , выберите защищенный блок с умом. Если у вас есть весь контент метода run()
в синхронизированном блоке, поток чтения должен ждать, пока обрабатываются результаты от курсора, что может занять некоторое время, - поэтому вы фактически теряете весь параллелизм .
Если вы решили пойти на синхронизированный блок, я хотел бы сделать следующие изменения (и я не утверждаю, что они совершенно правы):
Инициализировать поле списка таким образом мы можем синхронизировать доступ на него:
private List<String> mList = new ArrayList<>(); //initialize the field
Синхронизировать операцию модификации (добавить). Не читайте данные из курсора внутри блока синхронизации, потому что если его операция с низкой задержкой, во время этой операции не удалось прочитать миллиметр, который блокирует все остальные потоки довольно долго.
//mList = new ArrayList<>(); remove that line in your code
String data = cursor.getString(cursor.getColumnIndex(DataProvider.NAME)); //do this before synchronized block!
synchronized(mList){
mList.add(data);
}
Считанные итерации должен быть внутри блока синхронизации, так что ни один элемент не будет добавлен, в то же время итерации:
synchronized(mList){
for (String name : mList) {
if (name.equals(query) {
return true;
}
}
}
Так что, когда два потока работают в списке, один поток может либо добавить одиночный элемент или перебирать по всему списку за раз. У вас нет параллельного выполнения в этих частях кода.
Относительно синхронизированных версий списка (то есть Vector
, Collections.synchronizedList()
). Они могут быть менее эффективными, поскольку при синхронизации вы фактически теряете параллельное выполнение, так как только один поток может запускать защищенные блоки за раз. Кроме того, они все еще могут быть подвержены ConcurrentModificationException
, что может происходить даже в одном потоке. Он бросается, если структура данных изменяется между созданием итератора и итератором. Таким образом, эти структуры не решают вашу проблему.
Я также не рекомендую ручную синхронизацию, потому что риск просто ошибиться слишком высок (синхронизация на неправильном или другом мониторе, слишком большие блоки синхронизации, ...)
TL; DR
использовать java.util.concurrent.CopyOnWriteArrayList
Я бы предпочел также 'CopyOnWriteArrayList', но OP сказал« без копирования ». Может быть, он хочет гарантировать, что «только один поток может запускать защищенные блоки за раз». – beatngu13
«не копирование» -реквизиции своего рода бессмысленно. Конечно, CopyOnWriteArrayList * * копирует содержимое, но это часть деталей реализации. Единственной альтернативой может быть синхронизированный блок, синхронизирующий на самой карте, который подвержен ошибкам, поскольку он инициализируется символом «null». Использование 'Vector' не предотвращает' ConcurrentModificationException', потому что оно не имеет ничего общего с параллелизмом (оно вызывается, когда итератор пытается продолжить, но список был изменен после создания итератора) –
Спасибо за объяснение. Я думаю, что 'CopyOnWriteArrayList' лучше для моего случая (так как я обращаюсь к полю в потоке пользовательского интерфейса). – Nikolai