android スレッドセーフはじめの一歩(4)

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<>());

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()); } }

CopyOnWriteArrayList

次のように使います。

List<String> list = new CopyOnWriteArrayList<>();

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()); }

タイトルとURLをコピーしました