Java:排他制御がかからないと失敗するサンプルコード
必要にかられてせっせこjavaを勉強しています。
最近はマルチスレッドの学習を進めているのですが、
理解がイマイチなので今まで理解したところをメモ。
テキストは独習JAVA 9-3
テキストにはサンプルコードとして正しいものしか
書かれていないので理解に苦しみました。
なので駄目な方も書いてみたらだいぶ理解が進みました。
やはり詰まったら何事も手を動かすことは重要だね。
以下のコードでSynchronizedをつけたコメントアウトしてる方のコードは問題なく値は定まるが、つけていないほうで実行すると値は定まらない。
何故ならばdepositメソッドがロックが掛かっていないと、並列処理中に2回やったはずなのに1回分がなかったことにされる事が発生してしまうから。
詳細はパーフェクトJavaの図16.3で書かれている。
package main; public class Account { private int balance = 0; void deposit(int amount){ // synchronized void deposit(int amount){ balance += amount; } int getBalance(){ return balance; } } class Customer extends Thread { Account account; Customer(Account account){ this.account = account; } public void run() { try { for (int i = 0; i < 10000; i++) { account.deposit(10); } } catch (Exception e) { e.printStackTrace(); } } } class BankDemo{ private final static int NUMCUSTOMERS = 10; public static void main(String args[]){ //口座の作成 Account account = new Account(); //スレッドの作成と起動 Customer customers[] = new Customer[NUMCUSTOMERS]; for (int i = 0; i < NUMCUSTOMERS; i++) { customers[i] = new Customer(account); customers[i].start(); } //スレッドの完了を待機する for (int i = 0; i < NUMCUSTOMERS; i++) { try { customers[i].join(); } catch (Exception e) { e.printStackTrace(); } } //口座の残高を表示する System.out.println(account.getBalance()); } }
いまいちまだわかっていないのはSynchronizedブロックを利用する際にどこのスコープでブロック指定を行えばいいのかという点。
たとえば、上記のコード内のmain関数を
public static void main(String args[]){ //口座の作成 Account account = new Account(); synchronized(account){ //スレッドの作成と起動 Customer customers[] = new Customer[NUMCUSTOMERS]; for (int i = 0; i < NUMCUSTOMERS; i++) { customers[i] = new Customer(account); customers[i].start(); } //スレッドの完了を待機する for (int i = 0; i < NUMCUSTOMERS; i++) { try { customers[i].join(); } catch (Exception e) { e.printStackTrace(); } } } //口座の残高を表示する System.out.println(account.getBalance()); }
とaccountをロックしていても値は定まらない。
だけどrun()の方を以下のようにSynchronizedブロックを行うと値が定まる。
class Customer extends Thread { Account account; Customer(Account account){ this.account = account; } public void run() { try { for (int i = 0; i < 10000; i++) { synchronized(account){ account.deposit(10); } } } catch (Exception e) { e.printStackTrace(); } } }
ここら辺を説明できるようにしておくこと