ArrayListはスレッドセーフではないんですね。次の2つの問題があります。
(1)add()やremove()がスレッドセーフではない。
複数スレッドから同時に、add()やremove()をするようなプログラムを試したところ、size()と内容が合わなくなることがありました。
(2)イテレーション操作の途中で、add()やremove()すると、ConcurrentModificationExceptionが発生します。なお、同じスレッドでも発生します。同じスレッドなら、すぐに気づくので、イテレーション途中のadd()やremove()は書くことはないんですね。
Collections.synchronizedList
(1)については、synchronizedブロックで、add()やremove()を囲む方法があります。しかし、全ての箇所のadd()やremove()をsynchronizedブロックで囲む必要があります。
そこで、synchronizedListを次のように使います。
List<String> list = new ArrayList<>();
↓
List<String> list = Collections.synchronizedList(new ArrayList<>());
Code language: PHP (php)
listは、add()やremove()など、要素数を変更する操作は、スレッドセーフになりました。
ただし、synchronizedListでも、(2)のConcurrentModificationExceptionは発生します。
この問題には、synchronizedブロックで囲むか、CopyOnWriteArrayListを使います。
synchronized (list) {
for (String item : list) {
System.out.println(item);
}
}
synchronized (list) {
Iterator<String> ite = list.iterator();
while (ite.hasNext()) {
System.out.println(ite.next());
}
}
Code language: PHP (php)
CopyOnWriteArrayList
次のように使います。
List<String> list = new CopyOnWriteArrayList<>();
Code language: PHP (php)
add()やremove()はスレッドセーフですし、イテレーション途中のadd()やremove()をしても、ConcurrentModificationExceptionは発生しません。
ただし、イテレータを取得後、Listに変更を加えても、イテレータに反映されません。
List<String> list = new CopyOnWriteArrayList<>();
list.add("A");
Iterator<String> ite = list.iterator();
list.add(0, "B");
list.add("X");
// "B"ではなく、"A"が表示される。
// "X"は表示されない。
while (ite.hasNext()) {
System.out.println(ite.next());
}
Code language: PHP (php)