0
0

More than 3 years have passed since last update.

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

Posted at

コレクションの注意点

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

実行結果

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つの処理を行える
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("削除完了");
    }
}

実行結果

削除完了

ArrayListクラスとSetインターフェイスの拡張

  • CopyOnWriteArrayListクラス→配列のコピーを作成し、スレッドセーフを実現する
  • CopyOnWriteArraySetクラス→内部的にはCopyOnWriteArrayListを使用して、スレッドセーフを実現する

ArrayListにおいてConcurrentModificationExceptionが発生する例

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

実行結果

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クラスの利用

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

実行結果

ThreadA : A
main : add()
ThreadA : B
ThreadA : C

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

ふりかえり

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0