Hatena::ブログ(Diary)

midousujikunの日記

2012-03-02

マルチスレッドプログラミングの必須条件



「増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編」のイントロダクション部分で重要な概念が書かれていたのでメモ.『マルチスレッドプログラミングの評価基準』という節で,そのタイトルが示すとおりマルチスレッドプログラムを書いた際に検討すべき 4 点について簡潔にまとめてある.その中でも特に重要な "安全性" と "生存性" について覚書き.

安全性

安全性 (safety) とは,オブジェクトを壊さないことです.これは正しく動くプログラムに必須の条件です.
(44 ページより引用)

これはつまり,オブジェクト設計者が意図したとおりにプログラムが動作するかということ.動作しない例としては,排他制御を行わずに複数スレッドを使ってある変数の処理を行った場合にデータの不整合が起こるなどが挙げられる.マルチスレッドプログラミングでは最新の注意を払わなければならない点である.複数のスレッドが利用しても安全性が保たれることをスレッドセーフと呼ぶらしい.

ただし,何でもかんでも安全性を追求すればいいとうものでもない.詳細については後述の生存性にて説明する,

生存性

生存性 (liveness) とは,必要な処理はいつか必ず行われることです.これも正しく動くプログラムに必須の条件です (生存性の代わりに活動性という訳語が使われる場合もあります).
(45 ページより引用)

安全性が守られているからといって,必ずしもプログラムがちゃんと動作するとは限らない.極端な話,プログラムが途中で動かなくなればオブジェクトの状態が変化することもなく,安全性は高いといえる.しかし,このような意図した処理を行わないプログラムに意味はない,そいういった意味で,この生存性という評価基準もマルチスレッドプログラミングを考えていく上で重要な概念である.

生存性と安全性のトレードオフの具体例としてデッドロックが挙げられる.例えばメソッドに synchronized を付加すれば安全性は保たれるかもしれないが,複数のスレッドが相手のロック開放を互いに待ってしまうような状態が起こり得る.このような状態をデッドロックと呼び,避けなければならないものである.

以上の2つがマルチスレッドプログラミングを行う上で必須の条件である.この他にも再利用性やスループットや応答性などのパフォーマンスも評価基準として考えられるが,とりあえず "安全性" と "生存性" の高いプログラムを書けるようになりたい.

Mac に Windows 8 を導入

Gizmodo の記事を参照しながら特に理由も目的もなく入れてみた.ここで Japanese 64bit をダウンロードして DVD に焼き,あとは Bootcamp に任せておけば自動的にインストールしてくれる.ところどころでプロダクトコードなどの入力を求められるけど.プロダクトコードは Gizmodo に掲載されていたものを使用したけれど,勝手に使っていいものなのだろうか.

タブレット製品などのタッチパネルを意識した作りなんだろうが,かなり UI が変わっていてびっくりした.面白いけど Office 製品をバージョンアップした時の様になれるまで相当時間掛かりそう.

スレッドの排他制御と実行順序



引き続き「増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編」 を読んでマルチスレッドプログラミングについて勉強している.現在は第一章『Single Thread Execution』部分を読んでいるところである.この章ではマルチスレッドプログラミングで最も基本となる,”1つのスレッドによる実行”をテーマとしている.ここでは一度に実行するスレッドは一つと限定されており,synchronized を使って排他制御を実現している.

ここでは,synchronized を用いた場合とそうでない場合の例題プログラムの比較を行い,排他制御の重要性を論じている.ここで紹介されている例題は,synchronized を使用することで排他制御を実現し,各スレッドでのデータ整合性を崩さずに繰り返しデータ更新を行うというものなのだが一つわからないことがある.

例題で作成した main メソッドは以下のようなものである.本来 Gate クラスと UserThread クラスがあるがここでは省略する.

/*
 TestSingleThreadedExecution.java
 */

public class TestSingleThreadedExecution {
    public static void main (String[] args) {
	System.out.println( "Testing Gate, hit CTRL+C to exit." );
	Gate gate = new Gate();

	Thread aliceThread = new UserThread ( gate, "Alice", "Alaska" );
	aliceThread.start();

	Thread bobbyThread = new UserThread ( gate, "Bobby", "Brazil" );
	bobbyThread.start();

	Thread chrisThread = new UserThread ( gate, "Chris", "Canada" );
	chrisThread.start();
    }
}

これを実行するとスレッドが生成され,

Alice BEGIN
Bobby BEGIN
Chris BEGIN

標準出力される,その後,各スレッドが繰り返し実行され,データの不整合が起こった場合にそれを出力するようなプログラムである.これを何度も実行していると,時々以下のように Chris と Bobby が作成する順番が逆になることがあった.

Alice BEGIN
Chris BEGIN
Bobby BEGIN

どうやらプログラムに書かれている順番とスレッド作成の順番は必ずしも一致するわけではないようである.これは当然 synchronized を付加して排他制御を実現した場合でも起こる.synchronized はメソッド実行をロックにより制御しているだけであり,順番までは制御することができない.

プログラムに書いたとおりにスレッドが作成されないとなると,なおのことマルチスレッドプログラミングは難しいなあという感じが強まった.まだスレッド1つずつしか実行していないのだけれど,複数スレッドを同時に実行するとなおのこと管理が難しいだろう.この順番の話も読み進めていくうちに解決されるのだろうか.