С помощью этого кода:
public class SynchroApp {
public static void main(String[] args) {
final List<String> unsyList = new ArrayList<>();
final List<String> syList = Collections.synchronizedList(unsyList);
TimerTask changeList = new TimerTask() {
boolean addElem = false;
@Override
public void run() {
// add / remove elements to keep size between 2 and 9
if (syList.size() < 2)
addElem = true;
else if (syList.size() > 8)
addElem = false;
if (addElem)
syList.add(String.valueOf(System.currentTimeMillis()));
else
syList.remove(0);
}
};
TimerTask reverseList = new TimerTask() {
@Override
public void run() {
try {
for (String s : syList)
s = new StringBuffer(s).reverse().toString();
} catch (Exception e) {
System.out.println("Exception: " + e);
}
}
};
new Timer().scheduleAtFixedRate(changeList, 0L, 30L);
new Timer().scheduleAtFixedRate(reverseList, 0L, 20L);
}
}
Почему я все еще получаю какое-то ConcurrentModificationException
на Iterator.next
?
EDIT: обновление элементов списка в reverseList
не работает (как поясняется в комментариях). Этот код должен работать как ожидалось:
for (int i = 0; i < syList.size(); i++)
syList.set(i, new StringBuffer(syList.get(i)).reverse().toString());
Даже большинство синхронизированных коллекций не любят модификации и итератор вместе. Из описания API Collections.synchronizedList:
Крайне важно, чтобы пользователь вручную синхронизировался по возвращенному списку при его итерации по нему:
List list = Collections.synchronizedList(new ArrayList());... synchronized (list) { Iterator я = list.iterator();//Must be in synchronized block while (i.hasNext()) foo(i.next()); }
Также: вместо этого вы можете использовать коллекции из java.concurrent. У них обычно есть более четкий подход к синхронизации.
Потому что вы изменяете список, идя на него.
Обратите внимание, что синхронизированный список только делает каждый из своих методов и методы его итератора синхронизированными. Итерация по синхронизированному списку по-прежнему является неатомной операцией, которая включает несколько вызовов синхронизированных методов. Если вы хотите сделать весь итерационный атом, вы должны его явно синхронизировать, используя список как блокировку:
synchronized (syList) {
for (String s : syList) {
s = new StringBuffer(s).reverse().toString();
}
}
Итератор и модификатор должны использовать synchronizedList(), чтобы иметь возможность блокировки/синхронизации на объекте.
next
звонка в том коде, который вы опубликовали. Обратите внимание, однако, что цикл приrun
reverseList
в данный момент ничего не делает (строки Java являются неизменяемыми).