筆者の場合、なんかのプログラムを組むときにイキナリコードを書き始めることは稀で、まずインターネッツをして、組みたいコードの常套手段やら、フレームワークやら、流行やらを調査する。で、マネをしながら学習していく。
世の中すごい人が多いもので、筆者がマネをするのがやっとのものを、ご自分で開発効率性なり保守性なりの観点で優れたコードとかを書いて公開してくれて、かつ懇切丁寧に解説してくれているサイトがたくさんある。
けど悲しいかな、筆者の頭は相当悪い。ステップバイステップで説明してくれているのに理解できない。ところが同じようなことを説明しているのに絵があると、なぜか理解できる。絵といってもパワポ的なえじゃなくてマンガ的なやつ。
で、今日読んだのがコレ。
「やる夫がデザインパターンをやるようです。」
いや、わかりやすい。なぜかわからんけど。といったら書いた人に失礼だけど。
で、前置きが長くなったけど、本題に入ると、上記リンク先ページの下部に、シングルトンパターンのサンプルコードがある。
そこに、見慣れない、というか、Javaを初めてさわってから5年経つ筆者が初めて見るキーワードがあった。それがvolatileである。
リンク先では、
=============
// volatile指定子はuniqInst変数をYaruoGlobalインスタンスに対して初期化する際に
// マルチスレッドがuniqInst変数を正しく処理できるよう保障するものです
=============
という注釈が入っている。
これがどういうことなのかよくわからなかったので調べてみた。
結果からいうと、「volatileをつけた変数に関わる処理は書いたとおりに処理される。」ということだった。
んなのあたりめーじゃねーか!思われる方もいるかもしれない。
いやいやそうじゃないんですって!
プログラマが書いたコードは、コンパイラによってバイトコードに変換される。で、このコンパイラもガキの使いじゃなくって、筆者のようなショボショボのプログラマが書くショボショボのコードを幾分かマシな形にしてくれながら、バイトコードに変換する。
例えば
======
hoge="aaaaaa";//①
hoge="bbbbbb";//②
System.out.println(hoge);
======
とかいうコードがあったら①の処理はなかったことにする、みたいな。これを"最適化"っていうのかな。
で、やる夫の例でいくとこの部分が最適化の対象になる。
==========
// 同期コスト削減のため、まずnullかどうか聞く
if( uniqInst == null ){
// nullなら同期チェックしてnullのままならインスタンス生成
synchronized (YaruoGlobal.class ){
if( uniqInst == null ){
uniqInst = new YaruoGlobal();
}
}
}
==========
これは、synchronizedは重たい処理なので、ある程度振り分けを行っておこう、という意図で書かれている。つまり処理の結果としては、1回目の「uniqInst == null」はあってもなくても変わらない。
しかし、アクセス頻度の高い処理になる場合は、この1回目の処理があるかないかでは、システム全体の負荷が変わってくる可能性はある。
けれど、さすがに今のコンパイラはそういった意図まで汲めるほど賢くはない。つまり、おそらくコンパイラは1回目の「uniqInst == null」は最適化によって削除される可能性がある。※検証はまた今度しますので、気が向いたらしますのであらかじめご了承ください。
そして、あなたは負荷も意識したコードを書いたと思っているのに、なぜか解放されないメモリが溜まっていくという現象にぶち当たる。当然コードを見てもわからない。コンパイル前のコードはそういった状況にならないように組まれている(ように見える)のだから。
こんな事態を防ぐのがvolatileなわけです。これをつけた変数が絡む処理はコンパイラの最適化の影響を受けない。これであなたもスーパーハカー。
いや、知っている人からすれば、んなもん常識だよ。と言われるかも知れないですが、筆者は5年間かわし続けてきたわけですからね。
ちょっとぐらい得意げにさせてくださいよ。
なお、筆者がvolatileの用途を理解したのは、このサイトを見てです。
http://proger.blog10.fc2.com/blog-entry-20.html
コメントする