■スレッド
スレッドとは、「道筋」や「糸」のような意味である。
プログラムの処理を実行する際、1度に1つの命令を実行していく処理をシングルスレッドと呼ぶ。
一方、1度に2つ以上の命令を同時に実行する処理をマルチスレッドと呼ぶ。
マルチスレッド処理で「synchronized」を付けたメソッドを呼び出しても、1度に1つのすれスレッドのみ実行可能となる。(処理が終わるまで、他のスレッドは順番待ちをする)
■作成するプログラムの概要
・ユーザクラスを100個インスタンスする。
・各ユーザクラスにて、カウンタクラスのカウント処理(+1する)を1000回実行する。
なお、カウントする対象は、各クラスで共通のものとする。
※カウンタの合計値は、100(ユーザクラス)×1000(カウント処理)=100000回となる想定。
・「join」メソッドを使用し、すべてのユーザクラスの処理が終了した後、最終的な合計値(100000)を出力する。
■フローチャート
▼カウントクラス
・カウントアップ処理(同期処理)
▼ユーザクラス(Threadを継承)
・runメソッド(オーバーライド)
▼メインクラス
■プログラム仕様
▼カウントクラス
・カウントアップ処理(同期処理)
処理名 | カウントアップ処理(同期処理) |
---|---|
処理概要 | カウンタを+1する。 ※カウンタは「カウントクラス」にて定義(グローバル) |
引数 | なし |
戻り値 | なし |
入力 | 処理内容 | 出力 |
---|---|---|
– | カウンタを+1する。 | – |
▼ユーザクラス(Threadを継承)
・runメソッド(オーバーライド)
処理名 | runメソッド(オーバーライド) |
---|---|
処理概要 | 「カウントクラス」の「カウントアップ処理」を1000回繰り返す。 |
引数 | なし |
戻り値 | なし |
入力 | 処理内容 | 出力 |
---|---|---|
– | ■ループ処理:1000回繰り返す。 |「カウントクラス」の「カウントアップ処理」実行。 ■ | – |
▼メインクラス
処理名 | メインクラス |
---|---|
処理概要 | ・100個のユーザクラスをインスタンスする。 ・各ユーザクラスについて、スレッド処理を実行する。 ・最終的なカウンタの合計値を出力する。 |
引数 | なし |
戻り値 | なし |
入力 | 処理内容 | 出力 |
---|---|---|
– | ユーザクラスを100個インスタンス | – |
– | ■ループ処理:100回繰り返す。 | – |
– | ■ループ処理:100回繰り返す。 | 【コンソール】 例外エラー |
– | 最終的なカウンタの合計数を出力する。 | 【コンソール】 カウンタの合計値 |
■サンプルコード
// カウントクラス
class Counter{
// 初期値0を設定
public static int count = 0;
// カウントアップ処理
public static synchronized void CountUp(){
count = count + 1;
}
}
// ユーザクラス(スレッドを継承)
class User extends Thread{
// runメソッドをオーバーライド
public void run() {
// 1000回処理繰り返す
for(int i = 0; i < 1000; i++) {
// カウントクラスのカウントアップ処理を実行
Counter.CountUp();
}
}
}
// メインクラス
public class MultiThreadSync {
public static void main(String[] args) {
// ユーザクラスを100個インスタンス
User[] users = new User[100];
// 100個のインスタンス分繰り返す
for(int i = 0; i < 100; i++) {
// 各ユーザクラスをインスタンス
users[i] = new User();
// 各ユーザクラスでスレッド処理実行
users[i].start();
}
// 100回繰り返す
for(int j = 0; j < 100; j++) {
try{
users[j].join();
}catch(Exception e){
System.out.println(e);
}
}
// 最終的に何回加算されたか出力する
System.out.println(Counter.count);
}
}
■実行結果
・本処理を5回実行した場合、合計値はすべて「100000」である。
100000
100000
100000
100000
100000
■補足:非同期処理の実行結果
「カウンタクラス」の「カウントアップ処理」について、「synchronized」を付与せずに非同期処理にて、5回実行すると以下のようになる。
96873
98045
99293
99962
100000
上記のような結果となる理由として、非同期の場合、ユーザAのカウントアップ処理時にユーザBの処理が割り込みが発生するためである。
※割り込みが発生した場合、共通のカウンタの値を参照し、同じカウンタの値(例:6)を取得し、加算(+1)してカウンタ値(例:7)としてしまうため。
■参考:同期、非同期処理イメージ
(例)2つユーザが共通のカウンタをそれぞれ100回加算(+1)する処理
※非同期処理は1回だけ、ユーザA、Bの処理が同時に発生したものとする。
同期処理の場合、ユーザAが加算処理を行なっている間は、ユーザBの加算処理は実行されない。
したがって、ユーザA、Bの処理が同時に実行されることはないため、共通のカウンタを使用しても、合計カウンタ値は、200となる。
非同期処理の場合、ユーザAとユーザBの加算処理が同時に発生する場合がある。
したがって、上図の通り、ユーザAの3回目の処理と、ユーザBの1回目の処理が同時に実行される。
同時に実行された場合、ユーザA、Bは、同じカウンタの値「3」を参照し、加算処理(+1)を行うことで、「4」の結果が出力されてしまう。
結果として、1度同じ値について加算処理を行なったため、最終的な合計値は「199」となる。
コメント