スレッドを読み解く!
サーバサイドJavaアプリケーションでは、スレッドを意識することもあまりありません。しかし、クライアントアプリケーションやGUIを使用しているアプリケーションなどでは、画面表示と処理を平行して行ったり、同期を取ったりなど、マルチスレッド処理が必要になってきます。
読者のみなさんの中には「マルチスレッドプログラミングは難しい」そんなイメージを持たれている方は多いのではないでしょうか?
確かに、シングルスレッドの場合と比べて考慮すべき点(デッドロック・同期・リソースの競合など)が多くあります。また想定もしていなかった不思議な現象(変数の予期していない変更、タイミングによると思われる再現頻度の低い不具合)に悩まされてしまうこともあるでしょう。
例えば、2つのスレッドがあるとします。スレッドt1にはローカル変数r1があり、スレッドt2にはローカル変数r2があり、AとBの2つの共有する変数にアクセスできるものとします。はじめはA==B==0であるとします。
スレッドt1はr1にAを代入(命令1)、Bに1を代入(命令2)を実施します。スレッドt2はr2にBを代入(命令3)、Aに2を代入(命令4)を実施します。
我々の感覚からすると命令1と命令3が実行されて、次に命令2と命令4が実行されることを期待してしまいがちです。よって結果は、r1==1かつr2==1にはならないと思えます。しかし、実際にはr1==1かつr2==1となってしまうことがあります(必ずしもなるとも限りません)。
この話は言語仕様の「17.3 Incorrectly Synchronized Programs Exhibit Surprising Behaviors(同期化をあつかったプログラムは意外な振る舞いをみせる)」から引用した事例です。原因は次のように書かれています。
However, compilers are allowed to reorder the instructions in either thread,when this does not affect the execution of that thread in isolation.(訳:コンパイラは、各スレッドの実行を個別に解釈し、その実行に影響が及ばないと判断できた場合には、双方のスレッド中の命令を並べ替えることが許されているのである)。
すなわち、命令1と2を入れ替えても良いとコンパイラが判断したために、このような問題が起きたことが理解できます。これは、コードの同期化(後述)が不適切であったためです。
これらの動作は決してJavaVMがおかしい動作をしているわけではなく、むしろJavaVMがJava言語仕様に則った動作をしているからこそ起きることなのです。では、Java言語仕様ではスレッドの動作についてどのような規定をしているのでしょうか? その答えを求めて、Java言語仕様のスレッドの記述を読み解いていきましょう。 次のページ