2026年2月15日日曜日

【Java進化史 第17回】Java8 〜データが壊れた日。forEachが危ない理由〜

【Java進化史 第17回】Java8 〜データが壊れた日。forEachが危ない理由〜


昔、データが壊れたことがある。

テストでは動いていた。
だが本番で崩れた。

件数が合わない。
値が欠ける。
ときどき例外も出る。

原因は、スレッドセーフではないクラスだった。


■ スレッドセーフとは

複数のスレッドから同時に触っても、 データが壊れないこと。

逆に言えば、 同時に触ると壊れるクラスもある。

そして怖いのは、

テストでは再現しないことがある。

タイミング次第だからだ。


■ 並列処理は、昔は怖かった

Java7まで、 並列処理を書くには自分で守る必要があった。

synchronizedを付ける。
Concurrent系を使う。
共有変数に気を付ける。

だから私は、 並列処理が少し怖かった。


■ Streamでも同じことが起きる

List<String> result = new ArrayList<>();

list.parallelStream()
    .map(Employee::getName)
    .forEach(result::add);   // これ

parallelStreamにすると、 複数スレッドが同時にaddする可能性がある。

ArrayListはスレッドセーフではない。

つまり、 また壊れる可能性がある。


■ ああ、だからforEachは危ないのか

forEachは、 外の変数を触る。

外を触るということは、 共有するということだ。

共有は、並列では危ない。


■ collectという書き方

List<String> result =
    list.parallelStream()
        .map(Employee::getName)
        .collect(Collectors.toList());

collectは、 外の変数を使わない。

だからparallelでも、 安心して書ける。

仕組みの詳しい話までは知らなくていい。

並列にするなら、collectを書く。


■ Stream編、ここまで

最初は、 若手の1行が読めなかった。

そこから、

  • 左から読む
  • 何をやるかを書く
  • forEachは危ないことがある

ここまで来た。

もう、Streamは怖くない。

Java8は、 少しだけ味方になった。




0 件のコメント:

コメントを投稿

【Java進化史 第18回】Java8 〜「@」も読めなかった日

【Java進化史 第18回】Java8 〜「@」も読めなかった日〜 若手のコードを見たとき、 固まったことがある。 -> が読めない。 Streamも追えない。 そして、やたらと出てくる「@」。 なんじゃこれ? ■ @ は何者か 調べ...