TOPシステム開発> 【新・言語進化論】プロの言語仕様の読み方> 第3回:マルチスレッドなんて怖くない! (2/3)




【新・言語進化論】プロの言語仕様の読み方

【新・言語進化論】プロの言語仕様の読み方

第3回:マルチスレッドなんて怖くない!

著者:オープンストリーム 鍋倉 康宏

監修者:オープンストリーム 高安 厚思

公開日:2007/11/20(火)

Java言語仕様のスレッドを読み解く

Java言語仕様のスレッドに関する記述は「17 Threads and Locks(ロックとスレッド)」で説明されています。この章は以下のような構成になっています。



Java言語仕様のスレッドの章では特に「17.4 Memory Model(メモリ・モデル)」に大きく分量を割いて説明をしています。どちらかといえばJavaVM実装者向けの内容が色濃いですが、Javaプログラマとしても有益な情報が得られます。

17.4.1 Shared Variables(共有変数)」には以下のような記述があります。

Two accesses to (reads of or writes to) the same variable are said to be conflicting if at least one of the accesses is a write.(訳:同じ変数に対する2つのアクセス(読み込みあるいは書き込み)は、少なくとも一方のアクセスが書き込みである場合に競合していると表現される)。

確かに先ほどの例では、共有している変数(AやB)に対して書き込みをしていました。そして、「17.4.5 Happens-before Order(先行発生の順序)」には次のようにあります。

A program is correctly synchronized if and only if all sequentially consistent executions are free of data races.(訳:プログラムは、順序の整合性のある実行全てにデータ競合が含まれていない場合に限り、正しく同期化されることになる)。

If a program is correctly synchronized, then all executions of the program will appear to be sequentially consistent(訳:プログラムが正しく同期化されている場合、プログラムにおける全ての順序の整合性があるように見えることになる)。

まとめると、非常に奇妙で混乱を呼ぶ振る舞いは、言語仕様では「データ競合」により「正しく同期化」されていないプログラムであったということになります。すなわち、正しく同期化することによって、そのような振る舞いを防ぐことができるのです。

同期化については、「17.4.4 Synchronization Order(同期化の順序)」を読むと、ややプログラマ向きな記述があります。

この章にはモニタのロック、アンロック、スレッドの開始、各変数のへのデフォルト値の書き込み、スレッドにおける最後の動作、isAlive()やjoin()の呼び出し、割り込みなどが、どのような同期関係を生み出すのかについて記述されています。プログラマはこれらの同期関係を意識してプログラムを記述することで、正しく同期化していることを理解できるのです。

1ページ目の例では、共有している変数A、Bに対して同期処理がなされていません。そこで、変数A、Bのモニタに対してロックをかけて読み書きをすることにより、スレッド1とスレッド2に同期関係を持たせます。こうすることで、問題となっていた命令2、4が命令1、3の後に処理されてしまう現象を回避できるのです。これは「17.4.5 Happens-before Order(先行発生の順序)」の以下の部分から読み取れます。

If we have two actions x and y, we write hb(x, y) to indicate that x happens-before y.(訳:二つの動作x、yがある場合、xがyより先行発生することを示すためにhb(x、y)と記述する)。

If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).(訳:xとyが同じスレッドにおける動作であり、プログラムの順序においてxがyの前にある場合hb(x, y)となる)。

If an action x synchronizes-with a following action y, then we also have hb(x, y).(訳:動作xが続く動作yと同期関係にある場合、hb(x、y)となる)。

If hb(x, y) and hb(y, z), then hb(x, z).(訳:hb(x、y)かつhb(y、z)である場合、hb(x、z)となる)。

我々が日常何気なく使っている、synchronizedやwait()などには、このような意味があったのです。これらはJavaでスレッドを意図した通りに制御できることを明示しています。このように言語仕様には、我々が通常普通に使用している処理の定義がしっかりと記述されているのです。

マルチスレッドプログラムは非常に難しい面があり、マルチスレッドに関する書籍が刊行されるほどです。それらの書籍は大変有益でありますが、すべての事象の理由は言語仕様に記述されているこの「17.4 Memory Model(メモリ・モデル)」にあるのです。マルチスレッドを理解するためにも、是非通読しておくことをお薦めいたします。

スレッド実装の実際

言語仕様「17.4 Memory Model(メモリ・モデル)」には、以下のような記述があります。

The memory model describes possible behaviors of a program.An implementation is free to produce any code it likes,as long as all resulting executions of a program produce a result that can be predicted by the memory model.(訳:メモリ・モデルによって、プログラムの考えられる得る振る舞いを説明付けることができる。プログラムの実行結果がこのメモリ・モデルによって予測できる限り、実装者は自由にコードを生成することができる)。

すなわち、JavaVM実装者は言語仕様、例えばスレッドであればメモリ・モデルが正しく規定されていれば、どのような(JavaVMの)実装をしていてもよいということになっています。また、JavaプログラマはどのようなJavaVMであってもメモリ・モデルを理解していれば、プログラムは同じように動くことが保障される、ということです。

例えば、Linux向けJavaVMのスレッド実装の代表的なところには、グリーンスレッド(green thread)、ネイティブスレッド(native thread)、LinuxThreads、NPTL(Native POSIX Threads Library)があります。

グリーンスレッドはJavaVM内部で仮想的なスレッド(例えば、時分割になどにより)を作り実行します。一般的には高いパフォーマンスが期待できません。また、最近はあまり利用されていません。

ネイティブスレッドはJavaVMがプラットフォームのライブラリなどを利用しているスレッド実装です。

ネイティブスレッドの1つであるLinuxThreadsは古いスレッド実装です。スレッド1つ1つがプロセスのように見えます(psコマンドなどで確認すると、複数プロセス表示される)。LinuxThreadsの実装にはPOSIX.1仕様(注1)に準じていない点があります。

ネイティブスレッドの1つであるNPTLはLinuxThreadsと比べて新しい実装です。POSIX.1(注1)の要求仕様への準拠の度合いが高くなっています。多くのスレッドを生成しても比較的高性能です。NPTLを利用するにはLinuxカーネル2.6に実装されている機能が必要となります。

スレッド実装の詳細についてはここでは解説いたしませんが、他プラットフォームでのスレッド実装、サードパーティによるJavaVMによるスレッド実装など、さまざまな情報がWeb上に存在します。興味のある方は、ぜひ調べてみてください。

※注1:POSIX(Portable Operating System Interface)
IEEEにより定められている、UNIX系OSでの移植性の高いアプリケーション開発を容易にするためのAPI規定

次のページ




株式会社オープンストリーム 鍋倉 康宏
著者プロフィール
株式会社オープンストリーム 鍋倉 康宏
ソフトウェアエンジニアリングラボラトリ システムズアーキテクト
最近は主にSOA関連の調査研究、業務に従事。Hu-Basic、Cの次に学生時代Lispに接触。以来17年、各種関数型言語に魅せられ続けている。Debian GNU/Linuxと英語配列キーボードを愛好。休日は能楽鑑賞と神社仏閣巡礼に繰り出している。最近の関心領域は位相幾何学、圏論、伊勢神宮と鹿島神宮。
http://www.opst.co.jp/


株式会社オープンストリーム 高安 厚思
監修者プロフィール
株式会社オープンストリーム 高安 厚思
テクニカルコンピテンシーユニット 主管システムズアーキテクト
横浜国立大学経営学部卒。銀行系シンクタンクでオブジェクト指向技術の研究に携わった後、大手SIerにてアーキテクチャ構築、プロセス研究に携わった。現在株式会社オープンストリームにてSOAを中心とする研究開発およびアーキテクチャ構築に従事。最近はXMLのダイナミックさに魅了されている。
http://www.opst.co.jp/


INDEX
第3回:マルチスレッドなんて怖くない!
  スレッドを読み解く!
Java言語仕様のスレッドを読み解く
  プラットフォームに密接に関係しているスレッド