Java Concurrency, A(nother)
Peek Under the Hood
David Buck
日本オラクル
JavaDay Tokyo 2016
自己紹介
バック デイビッド
• Java SE サステイニング エンジニアリング
• JVM のバグを直す人
• OpenJDK 8 Updates
Project Maintainer
• 趣味:プログラミング
予定
• はじめに
• 背景
– 余談その1:特殊相対性理論
• 歴史
• 実装
– 余談その2:HSDIS
• 将来
• まとめ
はじめに
動機
「私が並行処理のコードを書かないのに。。。」
Web サーバ
picture: Coolcaesar at the English language Wikipedia [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC-BY-SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0/)]
GUI
ライブラリ
picture: David Vignoni / ICON KING (http://icon-king.com) [LGPL (http://www.gnu.org/licenses/lgpl.html) or LGPL (http://www.gnu.org/licenses/lgpl.html)]
バッチ処理
picture: US Social Security Administration (http://www.ssa.gov/history/acalcs.html) [Public domain]
「私が並行処理のコードを書かないのに。。。」
「私が並行処理のコードを書かないのに。。。」
競合状態
picture: Sakurambo at English Wikipedia [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC-BY-SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0/)]
ハイゼンバグ
Bug Heisenberg
Heisenbug
picture: Bundesarchiv, Bild 183-R57262 / Unknown / CC-BY-SA 3.0 [CC BY-SA 3.0 de (http://creativecommons.org/licenses/by-sa/3.0/de/deed.en)]
観察者効果
picture: Christian Schirm (Own work) [CC0]
ハイゼンバグを防ぐために
• Java Memory Model
• synchronized キーワード
• java.util.concurrent
ハイゼンバグを防ぐために
• Java Memory Model
• synchronized キーワード
• java.util.concurrent
• XXX をやっちゃ駄目!
• YYY をしなっきゃ!
a² + b² == c²
picture: WTF Public License, Version 2
背景
メモリモデル
メモリモデル
定義その1
書き込まれた値が正しく読み込まれる条件を
明確にする仕様
void hogeMethod1() {
int localA = 42;
assert localA == 42;
}
static int staticA;
void hogeMethod2() {
staticA = 42;
assert staticA == 42;
}
int data = 0;
boolean ready = false;
void hoge3() {
while (!ready) {};
assert data == 42;
}
void hoge4() {
data = 42;
ready = true;
}
犯人その1
犯人その2
メモリオーダリング
void hoge5() {
a = 1;
b = 2;
c = 3;
d = 4;
e = 5;
a = a + 1;
}
メモリオーダリング
void hoge5() {
a = 1;
b = 2;
c = 3;
d = 4;
e = 5;
a = a + 1;
}
void hoge5() {
a = 2;
b = 2;
c = 3;
d = 4;
e = 5;
}
メモリオーダリング
void hoge5() {
a = 1;
b = 2;
c = 3;
d = 4;
e = 5;
a = a + 1;
}
void hoge5() {
b = 2;
c = 3;
d = 4;
e = 5;
a = 2;
}
原則
シングルスレッドコードの振る舞いが
変わらない
アウト・オブ・オーダー実行
picture: Amit6, original version (File:Superscalarpipeline.png) by User:Poil (Own work) [CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0)]
CPUの相違
Type Alpha ARMv7
PA-
RISC
POWER
SPARC
RMO
SPARC
PSO
SPARC
TSO
x86
x86
oostore
AMD64 IA-64 zSeries
load-
load
Y Y Y Y Y Y Y
load-
store
Y Y Y Y Y Y Y
store-
store
Y Y Y Y Y Y Y Y
store-
load
Y Y Y Y Y Y Y Y Y Y Y Y
Atomic
(loads)
Y Y Y Y Y
Atomic
(stores)
Y Y Y Y Y Y
Depend
ent
loads
Y
instruct
ion
cache
Y Y Y Y Y Y Y Y Y Y
chart source: https://en.wikipedia.org/wiki/Memory_ordering
CPU次第
緩い 厳しい
Alpha
X86
メモリバリア
int data = 0;
boolean ready = false;
void hoge3() {
while (!ready) {};
assert data == 42;
}
void hoge4() {
data = 42;
ready = true;
}
メモリバリア
int data = 0;
boolean ready = false;
void hoge3() {
while (!ready) {};
assert data == 42;
}
void hoge4() {
data = 42;
ready = true;
}
メモリバリア
int data = 0;
boolean ready = false;
void hoge3() {
while (!ready) {};
assert data == 42;
}
void hoge4() {
data = 42;
ready = true;
}
メモリバリアの種類
• store-store
• store-load
• load-store
• load-load
コンパイラのバリア
• GCC
– __asm__ __volatile__("":::"memory");
• VC++
– _ReadBarrier
– _WriteBarrier
– _ReadWriteBarrier
余談その1:特殊相対性理論
picture: Sakurambo (Own work) [Public domain]
余談その1:特殊相対性理論
A
B
C
余談その1:特殊相対性理論
A
B
C
A, B, C
余談その1:特殊相対性理論
A
B
C
A, B, C C, B, A
余談その1:特殊相対性理論
基準系によって見る順番が異なる
A
B
C
A, B, C C, B, A
唯一の正しいタイムラインの無さ
• 特殊相対性理論
– 各基準系によって順番が異なる
唯一の正しいタイムラインの無さ
• 特殊相対性理論
– 各基準系によって順番が異なる
• マルチスレッド処理
– スレッドによって観察する順番が異なる
メモリモデル
定義その2
• 開発者が頼ってもいい振る舞いを明確にする
メモリモデル
定義その2
• 開発者が頼ってもいい振る舞いを明確にする
• プラットフォームがやってもいい最適化を制限
する
歴史
Java Memory Model (JMM)
• Write once, run
anywhere
• 1995
• 言語仕様(JLS)の一部
• Happened-before
picture: Peter Campbell [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC BY-SA 4.0-3.0-2.5-2.0-1.0 (http://creativecommons.org/licenses/by-sa/4.0-3.0-2.5-2.0-1.0)]
happened-before
Leslie Lamport氏
• LaTex
• happened-before
picture: Leslie Lamport [GFDL (http://en.wikipedia.org/wiki/GFDL]
happened-before
void hoge5() {
a = 1;
b = 2;
c = 3;
d = 4;
e = 5;
a = a + 1;
}
happened-before
void hoge4() {
data = 42;
ready = true;
}
void hoge3() {
while (!ready)
{};
assert data ==
42;
}
happened-before
void hoge4() {
data = 42;
ready = true;
}
void hoge3() {
while (!ready)
{};
assert data ==
42;
}
happened-before
void synchronized
hoge4() {
data = 42;
ready = true;
}
void synchronized
hoge3() {
while (!ready)
{};
assert data ==
42;
}
警告:hoge3 が先に実行されるとデッドロックが発生する
happened-before
void synchronized
hoge4() {
data = 42;
ready = true;
}
void synchronized
hoge3() {
while (!ready)
{};
assert data ==
42;
}
警告:hoge3 が先に実行されるとデッドロックが発生する
JMM 仕様
元 JMM のキーワード
• synchronized
– 排他制御 (mutual exclusion)
– happened-before
元 JMM のキーワード
• synchronized
– 排他制御 (mutual exclusion)
– happened-before
• volatile
– 値の可視性
元 JMM の問題点
• volatileが happened-before を成立しない
• final の値が変わる
• 有利な最適化方法が禁じられていた
Doug Lea氏
• Java のマルチスレッド
の聖書の執筆
• High-level の並行処理
の Java ライブラリを公
開した
Java SE 5.0
• 現代の JMM
• Lea氏の並行ライブラリが追加される
picture: Zvi Roger [CC BY 3.0 (http://creativecommons.org/licenses/by/3.0)]
現代の JMM
• JSR-133
• 元 JMM の問題点を解決
– volatile が happens-before を成立
– final の値が変わらない
現代 JMM のキーワード
• synchronized
– 排他制御
– happened-before
• volatile
– 値の可視性
– happened-before
• final
– イミュータブル
volatile != atomic
volatile int id = 0;
int incID() {
return id++;
}
JSR-133 の volatile
void hoge4() {
data = 42;
ready = true;
}
void hoge3() {
while (!ready)
{};
assert data ==
42;
}
警告:hoge3 が先に実行されるとデッドロックが発生する
volatile boolean ready = false;
happened-before
• ロックの取得・開放
• volatile 読み込み・書き込み
• final “freeze” (コンストラクタ)
• 外部処理 (JNI)
– System.out.println()
– 入出力
• スレッド start()/join()
• j.u.concurrent の collections の操作
java.util.concurency
• JSR-166
• Doug Lee 氏のライブラリを導入
並行処理の制御
• java.util.concurency
• synchronized
– wait()/notify()
• volatile / final
抽象的
具体的
Fork/Join (JDK 7)
関数型プログラミング(JDK 8)
• ラムダ式
• Stream API
λ
実装
余談その2:HSDIS
• JITが生成するコードの可視化
• 逆アセンブラのプラグインが必要
– GNU binutils ベース
– base-hsdis
• コマンドライン引数
– +PrintAssembly
– +CompileCommand=print,*MyClass.myMethod
Demo
将来
JEP-188/JMM9
• JVM レベルの仕様
• C11/C++11のモデルとの互換性
• JDK 6以降の新機能を含む
– (例AtomicX.weakCompareAndSet())
まとめ
高いレベルの抽象化を利用する
• java.util.concurency
• synchronized
– wait()/notify()
• volatile / final
抽象的
具体的
仕様に対して開発する
• 稀にしか発生しない問題が多い
• JRE や プラットフォームによって変わる
• 避けるべき行為
– synchronized (new Object()) { }
– 動くようになるまで闇雲に volatile を追加する
テストの方針
• 出来るだけ本番環境と近い環境でテストする
– 同じハード
– 同じ JRE (バージョンも)
– 同じ設定
Java Concurrency in Practice
• Java 並行処理の聖書
• JSR-133 もカバー
まとめ
• 高いレベルの抽象化を利用する
• 仕様に対して開発すること
• テストは不可欠
• JCiP を読むこと
ありがとうございます!
参照
• [ JSR 133 (Java Memory Model) FAQ ]
https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html
• [ Java Concurrency in Practice ]
http://jcip.net/
• [ Concurrent Programming in Java ]
http://gee.cs.oswego.edu/dl/cpj/
• [ The JSR-133 Cookbook for Compiler Writers ]
http://gee.cs.oswego.edu/dl/jmm/cookbook.html
• [ PrintAssembly ]
https://wiki.openjdk.java.net/display/HotSpot/PrintAssembly
• [ BASIC DISASSEMBLER PLUGIN FOR HOTSPOT DOWNLOADS ]
https://kenai.com/projects/base-hsdis/downloads

Java Concurrency, A(nother) Peek Under the Hood [Java Day Tokyo 2016 3-C]