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:\> 今度は上手く終了しました。
© Copyright 2024 ExpyDoc