【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は、 少しだけ味方になった。