2007年10月15日月曜日

スレッドの同期化

複数のスレッドからアクセスされるオブジェクトのメソッドは同期化処理をするべきです。
同期化は synchronized 修飾子を使用します。

下記のプログラムは 2人が同じ講座からお金を引き出すプログラムです。
残高が 10 未満なら引き出しをしません。
残高が 10 以上なら 10 ずつ引き出しをします。
したがって残高がマイナスになることはありません。

<サンプル>
public class Sample {
public static void main(String[] arvs) {
Account account = new Account(100);
Family taro = new Family(account, "太郎");
Family jiro = new Family(account, "次郎");
taro.start();
jiro.start();
}
}
class Family extends Thread {
private Account account;
private String name;
Family (Account account, String name) {
this.account = account;
this.name = name;
}
public void run() {
for (int i = 0; i < savings =" savings;">= amount) {
return true;
} else {
return false;
}
}
/* 引き出し処理の結果を返す */
public synchronized boolean withdraw(int amount) {
if (withdrawable(amount)) {
// 動きをわかりやすくするために1秒スリープ
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
savings -= amount;
return true;
} else {
return false;
}
}
public String toString() {
return String.valueOf(savings);
}
}

<出力結果>
太郎は引き出せました。残高 90
太郎は引き出せました。残高 80
太郎は引き出せました。残高 70
太郎は引き出せました。残高 60
太郎は引き出せました。残高 50
太郎は引き出せました。残高 40
次郎は引き出せました。残高 30
次郎は引き出せました。残高 20
次郎は引き出せました。残高 10
次郎は引き出せました。残高 0
次郎は引き出せませんでした。残高 0
次郎は引き出せませんでした。残高 0

同期化しない場合の出力結果
(例)
次郎は引き出せました。残高 80
太郎は引き出せました。残高 80
太郎は引き出せました。残高 60
次郎は引き出せました。残高 60
次郎は引き出せました。残高 50
太郎は引き出せました。残高 50
次郎は引き出せました。残高 30
太郎は引き出せました。残高 30
太郎は引き出せました。残高 10
次郎は引き出せました。残高 10
次郎は引き出せました。残高 -10
太郎は引き出せました。残高 -10

このように同期化をしないと残高がマイナスになるというおかしなことが起こります。


synchronized ブロックによる同期化
上記のコードは共通の Account オブジェクトをロックする形でも同期化できます。

public void run() {
synchronized (account) {
for (int i = 0; i < 6; i++) {
if (account.withdraw(10)) {
System.out.println(
name + "は引き出せました。残高 "
+ account.toString());
} else {
System.out.println(
name + "は引き出せませんでした。残高 "
+ account.toString());
}
}
}
}

この場合は実行中のスレッド以外は Account オブジェクトのすべてのメソッドへのアクセスができません。

同期化 = ロックの取得

ということができます。

0 件のコメント: