並列処理(マルチスレッド)についての備忘録③

## コレクションの注意点
- 拡張for文を用いてremove()メソッドで要素の削除をするとConcurrentModificationException例外が発生する
- 反復処理中にコレクションの変更の可能性がある場合は、例外を投げる仕様

```java
package cp8.no7;

import java.util.HashMap;
import java.util.Map;

public class Main {

	public static void main(String[] args) {

		Map<Integer, String> map = new HashMap<>();
		map.put(1, "test1");
		map.put(2, "test2");
		map.put(3, "test3");

		// 例外が発生する(ConcurrentModificationException)
		for (Integer key : map.keySet()) {
			map.remove(key);
		}

		// 順番に一つずつ削除するのは問題ない
//		map.remove(1);
//		map.remove(2);
//		map.remove(3);
	}
}

```

### 実行結果
```console
Exception in thread "main" java.util.ConcurrentModificationException
	at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1493)
	at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1516)
	at cp8.no7.Main.main(Main.java:16)
```

## マルチスレッド環境下におけるMapインターフェイスの拡張
- ConcurrentMapインターフェイスのメソッドを利用する
- putIfAbsent()メソッド→1回のロックでcontainsKey()メソッドで確認後、put()メソッドで格納するなど2つの処理を行える

```java
package cp8.no9;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Main {

	public static void main(String[] args) {

		Map<Integer, String> map = new ConcurrentHashMap<>();
		map.put(1, "test1");
		map.put(2, "test2");
		map.put(3, "test3");

		for (Integer key : map.keySet()) {
			map.remove(key);
		}

		System.out.println("削除完了");
	}
}

```

### 実行結果
```console
削除完了
```

## ArrayListクラスとSetインターフェイスの拡張
- CopyOnWriteArrayListクラス→配列のコピーを作成し、スレッドセーフを実現する
- CopyOnWriteArraySetクラス→内部的にはCopyOnWriteArrayListを使用して、スレッドセーフを実現する

## ArrayListにおいてConcurrentModificationExceptionが発生する例
```java
package cp8.no10;

import java.util.ArrayList;

public class Main {

	public static void main(String[] args) {

		ArrayList<String> list = new ArrayList<>();
		list.add("A");
		list.add("B");
		list.add("C");
		new Thread(() -> {
			for (String str : list) {
				System.out.println("ThreadA : " + str);
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();

		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		list.add("e");
		System.out.println("main : add()");
	}
}

```

### 実行結果
``` console
ThreadA : A
main : add()
Exception in thread "Thread-0" java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
	at cp8.no10.Main.lambda$0(Main.java:14)
	at java.base/java.lang.Thread.run(Thread.java:834)

```
## ConpyOnWriteArrayListクラスの利用
```java
package cp8.no11;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class Main {

	public static void main(String[] args) {

		List<String> list = new CopyOnWriteArrayList<>();
		list.add("A");
		list.add("B");
		list.add("C");
		new Thread(() -> {
			for (String str : list) {
				System.out.println("ThreadA : " + str);
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();

		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		list.add("e");
		System.out.println("main : add()");
	}
}

```

### 実行結果
```console
ThreadA : A
main : add()
ThreadA : B
ThreadA : C
```

※CopyOnWriteArrayListではイテレータを作成した時点の状態を参照するので、コンソール上には、追加したオブジェクトはリストに含まれていない。

## ふりかえり
- コレクションをマルチスレッド環境下で使う時には要素を追加したり削除する際には注意が必要
- Mapを使いたいときはConcurrentMapインターフェイス
- ArrayListを使いたいときはCopyOnWriteArrayList
- Setを使いたいときはCopyOnWriteArraySet