JAVA入門

JAVA入門後期②
スレッド処理(synchronizedの制御
同期・デッドロック)
同期①
1.同期①
データオブジェクトが一つあり、複数のスレッドがその
データを読み書きする場合を考えます。
このプログラムを「マルチスレッド」
データを「共有データ」と言います。
2人の人が稼いだお金を一つの口座にいれるとします。
このとき、
①スレッドで矛盾が出る例
②synchronized メソッドによる修正の例
を考えていくこととします。
同期①
class Kouza{
private int sum = 0;
public void add(int a)
{
int tmp = sum;
System.out.println("現在、合計額"+tmp+"円");
System.out.println("加算額"+a+"円");
tmp = tmp + a;
System.out.println("合計額"+tmp+"円");
sum = tmp;
}
同期①
class Workman extends Thread{
private Kouza kou;
public Workman(Kouza k){
kou = k;
}
public void run(){
for(int i=0;i<3;i++){
kou.add(50);
}
}
}
同期①
class Sample1{
public static void main(String args[]) {
Kouza kou =new Kouza();
Workman work1 = new Workman(kou);
work1.start();
Workman work2 = new Workman(kou);
work2.start();
}
}
同期①
work1.start();でスレッドが起動します。
最終結果としては合計額300円にならなければなり
ません。(50円を2人で3回口座に入れます。)
しかし次の実行結果によると、おかしなことになっ
ています。よくみますと途中に現在、合計額100円
が2回でいますし最終結果が150円です。
これはスレッド1の間にスレッド2が入り込んでいるた
めです。
実行結果①
現在、合計額0円
加算額50円
合計額50円
現在、合計額50円
加算額50円
合計額100円
現在、合計額100円
加算額50円
現在、合計額100円
加算額50円
合計額150円
現在、合計額150円
加算額50円
合計額200円
現在、合計額200円
加算額50円
合計額250円
合計額150円
スレッド1
スレッド2
スレッド1
スレッド2
<=残高がおかしいです。
同期①
class Kouzaを次のように書き換えます。
class Kouza{
private int sum = 0;
public synchronized void add(int a)
{
int tmp = sum;
System.out.println("現在、合計額"+tmp+"円");
System.out.println("加算額"+a+"円");
tmp = tmp + a;
System.out.println("合計額"+tmp+"円");
sum = tmp;
}
実行結果(2回目)
C:\Jsample2\20061007>java Sample2
現在、合計額0円
加算額50円
合計額50円
現在、合計額50円
加算額50円
合計額100円
現在、合計額100円
・・・・
現在、合計額200円
加算額50円
合計額250円
現在、合計額250円
加算額50円
合計額300円
同期①
こんどは正しい結果となっています。
メソッドにsynchronizedという指定をつけると、
あるスレッドがメソッドが処理している間は、他
のスレッドはこのメソッドを呼び出せなくなります。
このようにスレッド同士の処理のタイミングを取る
仕組みを同期をとるといいます。
同期②
同期その2
メソッドにsynchronized修飾子をつけて同期を
とる方法ではなく、オブジェクトにロックをかけて、
同期をとる方法を紹介します。次の例では、 account
というオブジェクトにロックをかけています。1つ目のス
レッドがaccountにロックをかけると、別のスレッドは
ロッ
クが解除されるまで、synchronizedで囲まれたブロック
(synchronizedブロックと呼びます)には、入ることがで
きなくなります。
例
// 口座クラス
class Account{
private int balance = 0;
void deposit( int amount ){
int a = balance;
a = a + amount;
try{
Thread.sleep((int)(Math.random()*10));
}catch(Exception e){}
balance = a;
}
int getBalance(){
return balance;
}
}
// 顧客クラス
class Customer extends Thread{
Account account;
// コンストラクタ
Customer(Account account){
this.account = account;
}
// 10円ずつ貯金することを1000回繰り返す
public void run(){
for( int i = 0 ; i < 1000 ; i++ ){
synchronized( account ){
account.deposit(10);
}
}
}
}
class DTest{
public static void main(String args[]){
// 口座を作る
Account account = new Account();
// 10人の顧客を作る
Customer customers[] = new Customer[10];
// 10人の顧客が1つの口座に振り込み処理を開始する
for( int i = 0 ; i < 10 ; i++ ){
customers[i] = new Customer(account);
customers[i].start();
}
// 10人のスレッドが終わるのを待つ
try{
for( int i = 0 ; i < 10 ; i++ ){
customers[i].join();
}
}catch(Exception e){
System.err.println("Error:"+e);
}
// 残高表示
System.out.println("残高:"+account.getBalance());
}
}
同期②
実行結果
残高:100000
デッドロック
デッドロック
デッドロックとは、マルチスレッドプログラミングで起こ
りやすいバグの1つです。 1つ目のスレッドがXという
オブジェクトをロックし、Yというオブジェクトのロックが
解除されるのを待ちます。2つ目のスレッドはYという
オブジェクトをロックしており、Xというオブジェクトの解
放を待ちます。このような場合、両方のスレッドはオブ
ジェクトの解放を永遠に待ち続けてしまうため、プログ
ラムは進まなくなり、いわゆる暴走の状態に陥ります
このように、複数のスレッドがお互いにロックの解放を
永久に待ち続けてしまうことをデッドロックといいます。
例
class X{
int x;
}
class Y{
int y;
}
class A extends Thread{
X x;
Y y;
A(X x, Y y){
this.x = x;
this.y = y;
}
void func1(){
System.out.println("func1:start");
synchronized(x){
synchronized(y){
try{
sleep(10);
}catch(Exception e){}
}
}
}
void func2(){
System.out.println("func2:start");
synchronized(y){
synchronized(x){
try{
sleep(10);
}catch(Exception e){}
}
}
}
public void run(){
for( int i = 0 ; i < 2 ; i++ ){
func1();
func2();
}
}
}
class TestD{
public static void main(String args[]){
X x = new X();
Y y = new Y();
A a[] = new A[3];
for( int i = 0 ; i < 3 ; i++ ){
a[i] = new A(x, y);
a[i].start();
}
for( int i = 0 ; i < 3 ; i++ ){
try{
a[i].join();
}catch(Exception e){}
}
}
}
実行結果
func1:start
func1:start
func1:start
func2:start
func2:start
ここでデッドロックが発生
Ctrl Cで解除
デッドロック
では、デッドロックを解除するにはどうしたらよ
いでしょう?
答えは、例えば
2番目の処理の順番をY→Xではなく、 X→Yと
することです。
具体的には例えば、func2()を次のように書き換
えます。
void func2(){
System.out.println("func2:start");
synchronized(x){
synchronized(y){
try{
sleep(10);
}catch(Exception e){}
}
}
}
実行結果
func1:start
func1:start
func1:start
func2:start
func2:start
func2:start
func1:start
func1:start
func1:start
func2:start
func2:start
func2:start
C:\>
今度は上手く終了しました。