2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

スレッドセーフではないJavaクラス一例と注意点

Posted at

スレッドセーフではない Java クラスとその注意点

Java開発者ではなじみがあるクラスだと思いますがマルチスレッド環境では注意点が必要なクラスを記載します。
スレッドセーフについて:
https://qiita.com/naoyoshinori/items/507c5c3ea6027033f4bb

本記事では ArrayListHashMapSimpleDateFormat について、それぞれのスレッドセーフでない理由と注意すべき使用例を解説します。

1. ArrayList

ArrayList は内部的に配列を使用しており、サイズ変更や要素の追加・削除時に競合が発生する可能性があります。複数のスレッドが同時に ArrayList を変更すると、データの不整合や ConcurrentModificationException が発生することがあります。

解決案

スレッドセーフな CopyOnWriteArrayList を使用する。

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

List<Integer> safeList = new CopyOnWriteArrayList<>();

2. HashMap

HashMap は内部で put()remove() を行う際に、バケットの再配置(リハッシュ)が発生することがあります。このとき、複数スレッドが同時に操作すると、データの不整合や無限ループが発生する可能性があります。

解決案

スレッドセーフな ConcurrentHashMap を使用する。

import java.util.concurrent.ConcurrentHashMap;

ConcurrentHashMap<Integer, String> safeMap = new ConcurrentHashMap<>();

3. SimpleDateFormat

SimpleDateFormat は、parse()format() を実行する際に内部状態を変更します。複数のスレッドが同時に format()parse() を呼び出すと、不正なフォーマットや ParseException が発生する可能性があります。

注意すべき使用例

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SimpleDateFormatExample {
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    public static void main(String[] args) {
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                try {
                    Date date = sdf.parse("2024-04-01");
                    String formattedDate = sdf.format(date);
                    System.out.println(formattedDate);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
    }
}

このコードでは、複数のスレッドが SimpleDateFormatparse() メソッドを同時に呼び出すため、データの競合が発生する可能性があります。

解決案

  1. DateTimeFormatter を使用する
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse("2024-04-01", formatter);

まとめ

クラス 発生可能性がある場面 解決策一例
ArrayList 要素の追加・削除時にデータ競合が発生 CopyOnWriteArrayListを使用
HashMap 同時書き込みでデータ競合が発生 ConcurrentHashMap を使用
SimpleDateFormat format()parse() の内部状態が変更される DateTimeFormatterを使用

今回は一例になりますがスレッドセーフの問題がある場合は性能などの検討も行い設計を確認したうえで適切な代替手段を選択をしましょう。

2
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?