スレッドの排他制御(synchronized)
複数のスレッドを起動させる場合、処理に矛盾が生じる場合があります。
今回の記事ではその例と解決方法を見ていきます。
上記のようにオークションにおける入札や最高額を管理するシステムがあったとします。
このサンプルを実行すると下記のような結果になります。
100円で6回入札しているはずですが、結果がまったく違っており、おかしなことになっているのがわかると思います。
これは2つのスレッドが同じフィールドに同時にアクセスしてしまっているせいで起こる現象です。
つまり原因は、Kaikeiクラスのmoneyインスタンス変数の変更を行っているaddメソッドを、2つのスレッドが同時に実行してしまっている点にあります。
このように複数のスレッドが同時にメソッドを実行してほしくないようなとき、そのメソッドに対してsynchronizedという指定をつけます。
※synchronizedを直訳すると同期という意味になります。
まずは下記サンプルのように、addメソッドにsynchronizedを指定して実行してみてください。
【実行結果】
実行結果に矛盾がなくなっていることがわかると思います。
このようにメソッドに対してsynchronizedという指定をつけると、一つのスレッドがメソッドを処理している間は、別のスレッドはこのメソッドを呼び出すことができなくなります。
今回の記事ではその例と解決方法を見ていきます。
class Kaikei{
private int money = 0;
public void add(int m){
int tmp = money;
System.out.println("現在の価格は" + money + "円です。");
System.out.println("ただいま" + m + "円の入札がありました。");
tmp += m;
System.out.println("最高額は" + tmp + "円となります。 \n");
money = tmp;
}
}
class Auction implements Runnable{
private Kaikei ki;
public Auction(Kaikei ki){
this.ki = ki;
}
public void run(){
for(int i = 0; i < 3; i++){
ki.add(100);
}
}
}
public class Syn{
public static void main(String[] args){
Kaikei ki = new Kaikei();
Auction auc1 = new Auction(ki);
Auction auc2 = new Auction(ki);
Thread th1 = new Thread(auc1);
Thread th2 = new Thread(auc2);
th1.start();
th2.start();
}
}上記のようにオークションにおける入札や最高額を管理するシステムがあったとします。
このサンプルを実行すると下記のような結果になります。
現在の価格は0円です。
現在の価格は0円です。
ただいま100円の入札がありました。
最高額は100円となります。
現在の価格は100円です。
ただいま100円の入札がありました。
最高額は200円となります。
現在の価格は200円です。
ただいま100円の入札がありました。
最高額は300円となります。
ただいま100円の入札がありました。
最高額は100円となります。
現在の価格は100円です。
ただいま100円の入札がありました。
最高額は200円となります。
現在の価格は200円です。
ただいま100円の入札がありました。
最高額は300円となります。
現在の価格は0円です。
ただいま100円の入札がありました。
最高額は100円となります。
現在の価格は100円です。
ただいま100円の入札がありました。
最高額は200円となります。
現在の価格は200円です。
ただいま100円の入札がありました。
最高額は300円となります。
ただいま100円の入札がありました。
最高額は100円となります。
現在の価格は100円です。
ただいま100円の入札がありました。
最高額は200円となります。
現在の価格は200円です。
ただいま100円の入札がありました。
最高額は300円となります。
100円で6回入札しているはずですが、結果がまったく違っており、おかしなことになっているのがわかると思います。
これは2つのスレッドが同じフィールドに同時にアクセスしてしまっているせいで起こる現象です。
つまり原因は、Kaikeiクラスのmoneyインスタンス変数の変更を行っているaddメソッドを、2つのスレッドが同時に実行してしまっている点にあります。
このように複数のスレッドが同時にメソッドを実行してほしくないようなとき、そのメソッドに対してsynchronizedという指定をつけます。
※synchronizedを直訳すると同期という意味になります。
まずは下記サンプルのように、addメソッドにsynchronizedを指定して実行してみてください。
class Kaikei{
private int money = 0;
synchronized public void add(int m){
int tmp = money;
System.out.println("現在の価格は" + money + "円です。");
System.out.println("ただいま" + m + "円の入札がありました。");
tmp += m;
System.out.println("最高額は" + tmp + "円となります。 \n");
money = tmp;
}
}
class Auction implements Runnable{
private Kaikei ki;
public Auction(Kaikei ki){
this.ki = ki;
}
public void run(){
for(int i = 0; i < 3; i++){
ki.add(100);
}
}
}
public class Syn{
public static void main(String[] args){
Kaikei ki = new Kaikei();
Auction auc1 = new Auction(ki);
Auction auc2 = new Auction(ki);
Thread th1 = new Thread(auc1);
Thread th2 = new Thread(auc2);
th1.start();
th2.start();
}
}【実行結果】
現在の価格は0円です。
ただいま100円の入札がありました。
最高額は100円となります。
現在の価格は100円です。
ただいま100円の入札がありました。
最高額は200円となります。
現在の価格は200円です。
ただいま100円の入札がありました。
最高額は300円となります。
現在の価格は300円です。
ただいま100円の入札がありました。
最高額は400円となります。
現在の価格は400円です。
ただいま100円の入札がありました。
最高額は500円となります。
現在の価格は500円です。
ただいま100円の入札がありました。
最高額は600円となります。
ただいま100円の入札がありました。
最高額は100円となります。
現在の価格は100円です。
ただいま100円の入札がありました。
最高額は200円となります。
現在の価格は200円です。
ただいま100円の入札がありました。
最高額は300円となります。
現在の価格は300円です。
ただいま100円の入札がありました。
最高額は400円となります。
現在の価格は400円です。
ただいま100円の入札がありました。
最高額は500円となります。
現在の価格は500円です。
ただいま100円の入札がありました。
最高額は600円となります。
実行結果に矛盾がなくなっていることがわかると思います。
このようにメソッドに対してsynchronizedという指定をつけると、一つのスレッドがメソッドを処理している間は、別のスレッドはこのメソッドを呼び出すことができなくなります。
