スレッドの排他制御
スレッドを複数持つことで、「同時に複数の処理を実行する」ということができ、作成できるプログラムの実現可能性が広がるわけですが、「同時に複数の処理を実行する」ことでときに、プログラムが意図しない動作をしてしまうことがあります。
例えば、同じインスタンスの同じフィールドを、複数のスレッドが同時に使用したらどうなるでしょうか。
前回のサンプルを少し改良したのが上記サンプルです。
上記では同じインスタンス(car1)でスレッドを二つ起動させています。
私の環境で実行した結果は下記のようになりました。
for文のループ回数は5回と設定しているのに、9回も処理しています。
おかしいように見えるかもしれませんが、2つのスレッドが同時に進行し、同じインスタンス変数を加算していっているわけですから、5回以上処理するのは自然な結果です。
しかし、だとすると10回処理しなければならないのに9回しか処理してません。
つまり、1回分の加算処理が正しく行われていないことになります。
では、下記のサンプルを実行したらどうなるでしょう。
【実行結果】
2つスレッドを起動したはずですが、スレッドが一つずつ処理されているのがわかると思います。
上記2つのサンプルの違いはsynchronizedをrun()メソッドに加えただけです。
次回、スレッドの排他制御についてもう少し詳しく見ていきます。
例えば、同じインスタンスの同じフィールドを、複数のスレッドが同時に使用したらどうなるでしょうか。
public class CarRun implements Runnable{
private String carName;
private int count;
public CarRun(String carName){
this.carName = carName;
}
public static void main(String[] args){
CarRun car1 = new CarRun("インサイト");
Thread th1 = new Thread(car1);
Thread th2 = new Thread(car1);
th1.start();
th2.start();
}
public void run(){
count = 0;
for(int i = 0; i < 5; i++){
try{
Thread.sleep(1000);
}catch(Exception e){}
this.addCount();
System.out.println(carName + " : " + count + "回目の処理です。");
}
}
public void addCount(){
count += 1;
}
}前回のサンプルを少し改良したのが上記サンプルです。
CarRun car1 = new CarRun("インサイト");
Thread th1 = new Thread(car1);
Thread th2 = new Thread(car1);
th1.start();
th2.start();上記では同じインスタンス(car1)でスレッドを二つ起動させています。
私の環境で実行した結果は下記のようになりました。
インサイト : 1回目の処理です。
インサイト : 1回目の処理です。
インサイト : 2回目の処理です。
インサイト : 3回目の処理です。
インサイト : 4回目の処理です。
インサイト : 5回目の処理です。
インサイト : 6回目の処理です。
インサイト : 7回目の処理です。
インサイト : 8回目の処理です。
インサイト : 9回目の処理です。
インサイト : 1回目の処理です。
インサイト : 2回目の処理です。
インサイト : 3回目の処理です。
インサイト : 4回目の処理です。
インサイト : 5回目の処理です。
インサイト : 6回目の処理です。
インサイト : 7回目の処理です。
インサイト : 8回目の処理です。
インサイト : 9回目の処理です。
for文のループ回数は5回と設定しているのに、9回も処理しています。
おかしいように見えるかもしれませんが、2つのスレッドが同時に進行し、同じインスタンス変数を加算していっているわけですから、5回以上処理するのは自然な結果です。
しかし、だとすると10回処理しなければならないのに9回しか処理してません。
つまり、1回分の加算処理が正しく行われていないことになります。
では、下記のサンプルを実行したらどうなるでしょう。
public class CarRun implements Runnable{
private String carName;
private int count;
public CarRun(String carName){
this.carName = carName;
}
public static void main(String[] args){
CarRun car1 = new CarRun("インサイト");
Thread th1 = new Thread(car1);
Thread th2 = new Thread(car1);
th1.start();
th2.start();
}
synchronized public void run(){
count = 0;
for(int i = 0; i < 5; i++){
try{
Thread.sleep(1000);
}catch(Exception e){}
this.addCount();
System.out.println(carName + " : " + count + "回目の処理です。");
}
}
public void addCount(){
count += 1;
}
}【実行結果】
インサイト : 1回目の処理です。
インサイト : 2回目の処理です。
インサイト : 3回目の処理です。
インサイト : 4回目の処理です。
インサイト : 5回目の処理です。
インサイト : 1回目の処理です。
インサイト : 2回目の処理です。
インサイト : 3回目の処理です。
インサイト : 4回目の処理です。
インサイト : 5回目の処理です。
インサイト : 2回目の処理です。
インサイト : 3回目の処理です。
インサイト : 4回目の処理です。
インサイト : 5回目の処理です。
インサイト : 1回目の処理です。
インサイト : 2回目の処理です。
インサイト : 3回目の処理です。
インサイト : 4回目の処理です。
インサイト : 5回目の処理です。
2つスレッドを起動したはずですが、スレッドが一つずつ処理されているのがわかると思います。
上記2つのサンプルの違いはsynchronizedをrun()メソッドに加えただけです。
次回、スレッドの排他制御についてもう少し詳しく見ていきます。
