スレッド


スレッドの作成方法

スレッドを作成する方法は、大きく分けて という2種類の方法があります。

Threadクラスをextendsする

Threadクラスをextendsして public void run(); メソッドを実装します。



RunPrintThread.javaの実行例
$ javac RunPrintThread.java
$ java RunPrintThread
thread 1
thread 1
...
thread 1
thread 2
...
thread 2
thread 1
...
thread 3

Runnable インターフェイスをimplementsする

既に他のクラスをextendsしている場合には、上記の Threadクラスをextendsする方法は取ることができません。 その場合は、Runnable インターフェイスをimplementsします。



RunPrintThread2.javaの実行例
[nitta@ni java]$ javac RunPrintThread2.java
[nitta@ni java]$ java RunPrintThread2
thread 1
...
thread 1
thread 3
...
thread 3
thread 2
...
thread 3

スレッドの一時停止

スレッドを一時停止させるには、Threadクラスのsleep()メソッドを使います。 メソッドの引数には一時停止させたい時間をミリ秒単位の整数で指定します。 一時停止中にInterruptExceptionが起きる可能性があるので、tryで 囲んでおく必要があります。



RunIntervalThread.javaの実行例
$ javac RunIntervalThread.java
$ java RunIntervalThread
thead 1   ←----------------+
thead 2   ←+               |
thead 2   ←+2秒置きに表示  |
thead 1   ←----------------+3秒置きに表示
thead 2
thead 1
thead 2
thead 2
thead 1
thead 2
thead 1
thead 2
thead 2

スレッドの排他制御

複数のスレッドが、特定のインスタンスを同時に操作すると、 場合により想定していない動作を引き起こすことがあります。 このような状況を「競合状態」(Race Condition)と呼びます。 同時に操作してはいけない部分を「きわどい領域」(Critical Section)と 呼び、また、同時に操作してはいけない部分を同時にアクセスできない ようにすることを「排他制御」(Mutual Exclusion)と呼びます。




RunBadBank.javaの実行例
$ javac RunBadBank.java ClientBadBank.java BadBank.java  
$ java RunBadBank 
$ java RunBadBank
money = -2000
money = -2000
[nitta@ni java]$ java RunBadBank  
money = 0
[nitta@ni java]$ java RunBadBank  
money = -1000
[nitta@ni java]$ java RunBadBank  
money = 0
money = 0
money = -1000
money = -1000
...

RunBadBank.javaを実行させると、場合により表示されるものが異なります。 競合状態が起きて、moneyが負の値になった場合にmoneyの値が表示されます。 "money=0"と表示されるのは if 文の判定のときにmoneyが負であったにも かかわらず、表示しようとしたときは0になったことを意味します。 また、何も表示されない場合でも、moneyが1000を超えた値になっている 場合がありえます。

BadBankクラスの代わりに、つぎのようなBankクラスやBank2クラスを 使うと正しく動作します。

synchronized (式) {...}で囲んだ、{ ... }部分のはロックが設定されて、 ロックを取得したスレッド1個しか同時には実行できません。 (式)の部分は、ロックを設定するインスタンスをあたえます。 インスタンス毎にロックが設定されます。


メソッドの先頭に synchronizedを宣言すると、そのメソッドは thisをロックを設定するためのインスタンスにしてメソッド 全体をsynchronizedにしたことになります。

synchronized void method() {
    ...
  }
=
void method() {
    synchronized (this) { ... }
}


デッドロックの回避




実行させると、aliceがspoonを、ベティがforkを持った瞬間に デッドロックが発生してしまいます。

RunEater.javaの実行例
$ javac RunEater.java Eater.java EatTool.java 
$ java RunEater 
aliceは左手に[spoon]を持った。
aliceは右手に[fork]を持った。
aliceは食べます。もぐもぐ
aliceは右手から[fork]を離した。
aliceは左手から[fork]を離した。
...(略)
aliceは左手に[spoon]を持った。
aliceは右手に[fork]を持った。
aliceは食べます。もぐもぐ
aliceは右手から[fork]を離した。
aliceは左手から[fork]を離した。
aliceは左手に[spoon]を持った。
bettyは左手に[fork]を持った。
                                ←ハングアップ


コンピュータネットワーク 演習


提出〆切は、次回の講義の開始時刻です。


課題4a

提出先 http://nw.tsuda.ac.jp/class/network/local/handin/list.php?id=kadai4a
提出ファイルRunBank.java, ClientBank.java Bank.java
コメント欄なし

RunBadBank.javaを動作させて、計算を間違うことを確認して下さい。

その後、RunBadBank.javaを変更して RunBank.javaを作成して下さい。 ClientBadBankやBadBankの代わりにClientBankやBankを呼び出すものとします。 Bank.javaにcheck()メソッドの定義と呼び出しを追加して、 正しく動作することを確認して下さい。 check()メソッドの呼び出しを追加する場所は、BadBank.javaと同じく withdraw()メソッド内です。


課題4b

提出先 http://nw.tsuda.ac.jp/class/network/local/handin/list.php?id=kadai4b
提出ファイル書き換えてデッドロックが起きなくなったEater.java
コメント欄なし

RunEater.javaを動作させて、デッドロックが起きることを確認して下さい。 デッドロックが起きた場合はCtl-C (Controlキーを押しながらcキーを押す) でプログラムの実行を中断して下さい。

RunEater.java や Eater.java を書き換えて、デッドロックが起きないようにして下さい。 (ヒント: アリスとベティが共通のオブジェクトでsynchronizeすればよい。 それにはRunEater.javaのmainで

        Object obj = new Object();
	new Thread(new Eater("alice",spoon,fork,obj)).start();
	new Thread(new Eater("betty",fork,spoon,obj)).start();
とやって、ベティがアリスと同じオブジェクトを参照できるようにする。

変更したプログラムでデッドロックが起きないことを確認して下さい。


課題4c

提出先 http://nw.tsuda.ac.jp/class/network/local/handin/list.php?id=kadai4c
提出ファイルLineCount.javaとRunLineCount.java
コメント欄 0個、1個、2個以上のファイルをコマンドラインで指定して実行させた実行結果
[実行例]
  $ java RunLineCount
  $ java RunLineCount Eater.java
  $ java RunLineCount Eater.java RunEater.java Bank.java

「テキストファイルのパス」(=ファイル名)がコマンド引数で与えられると、 各ファイル毎に何行あるかを数えてファイル名と共に表示するプログラム RunLineCount.javaを作成しなさい。 「テキストファイルのパス」は複数指定されることがある。 ファイル毎に、スレッドを作って並行に行を数えること (Runnableをimplementしたクラス LineCountを用意すること)。

RunLineCount.javaの実行例
$ javac RunLineCount.java LineCount.java 
$ java RunLineCount 
$ java RunLineCount RunLineCount.java LineCount.java 
RunLineCount.java: 8
LineCount.java: 24
$ java RunLineCount *.java 
LineCount.java: 24
EatTool.java: 5
Eater.java: 26
TransTextClient.java: 45
...