Javaのマルチスレッド間の値の変更と参照についてメモ
なぜダブルチェックロッキングがアンチパターンだと、こんなに強く書かれている書籍があるのに、それを使おうとする人たちがいるのだろうか(かつては僕もその1人だった) pic.twitter.com/8GHeObQjYg
— ぷーぷーぷー (@Pooh3Mobi) August 8, 2018
すべてはここから始まった?
のでとりあえず気になるとろを調べて。あーだいたいおkって感じのところまで。
参考になった書籍の宣伝。 Javaでマルチスレッドする場合は、とりあえずこれがあると困らない?
Java並行処理プログラミング ―その「基盤」と「最新API」を究める―
- 作者: Brian Goetz,Joshua Bloch,Doug Lea
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2006/11/22
- メディア: 単行本
- 購入: 30人 クリック: 442回
- この商品を含むブログ (174件) を見る
以下雑なメモ。
・スレッド間で共有される値は、高速化のためCPUのキャッシュメモリを参照することになるので、完全に共有されておらずタイミングによっては変更後と変更前があべこべで参照されることがある。 (なので、lockなりsynchronizedなりvolatileなりAtomicを使う必要がある)
・スレッド間で公開されるフィールド値の変更を、時系列上で適切に別のスレッドが変更後の値を参照するには(可視性)、フィールドに対してvolatileの修飾子で宣言したりすることで、スレッド間でライト→リードの順序関係が守られるようにする必要がある。 (事前発生関係というものがあり、データのレースコンディションが起きないためには、それをまもるパターンで実装する)
・volatile int counter = 0; counter++;みたいなことはしちゃだめ。操作のアトミック性が保たれていない。AtomicIntegerを使う方がマシ。
・AtomicIntegerなどは、単純計測ではlockを利用したものより遅いパフォーマンス試験の結果を出す可能性があるが、現実的なマルチスレッドでの争奪が起きた場合は、lockを使うよりパフォーマンスがよくなる確率が高いので、利用を考えるに値する。
・Atomicの特性を利用し、AtomicReferenceとCAS(compare-and-swap)によるノンブロッキングなキューやスタックを実現することができる。
・DCLはアンチパターン。volatileをつかったDCLはJava1.4以下ではつかえないし最近のJVMとマシンの性能なめんな、昔はともかく遅延初期化してまでうれしいことって何?
・コンストラクタによる初期化が終わるまではfinalizeは呼ばれない(みたいな事前発生関係のある順序が割とマルチスレッドでは大事)
あと以下もすんごい役に立った。
マルチコア時代に備えて本気でメモリモデルを理解しておこう - リオーダー & finalフィールド 編 - - じゅんいち☆かとうの技術日誌
今後困ったらここら辺また読み直したり、Java並行処理プログラミングの書籍を読み直すなりする。