これにも関わらず、思うような結果にならないケースがあります。
次は、検証用のサンプル(Java SE 5.0以降)です。
package a;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
class A implements Runnable {
//VectorはThread-safeである
private List<String> v = new Vector<String>();
public void run() {
for(int i=0; i<1000; i++){
//3桁の数字文字列を生成
final String s = createNumberString(i);
//文字列sがVectorに含まれていなければ、追加する
if(v.contains(s) == false){
v.add(s);
}
}
}
//3桁の文字列を生成する(例:1→"001")
private static String createNumberString(int i){
final StringBuilder sb = new StringBuilder(String.valueOf(i));
while(sb.length() < 3){
sb.insert(0, "0");
}
return sb.toString();
}
public static void main(String[] args) throws InterruptedException {
//多重度3でA.run()の処理を実行する
final A a = new A();
final Thread[] tt = new Thread[3];
for(int i=0; i<tt.length; i++){
tt[i] = new Thread(a);
tt[i].start();
}
for(Thread t : tt){
t.join();
}
//処理結果を出力
final List<String> v = a.v;
final String[] ss = v.toArray(new String[v.size()]);
Arrays.sort(ss);
for(String s : ss){
System.out.println(s);
}
}
}
上記プログラムでは、実行するたびに結果が変わりますが、次のように同じ数字が2か3個出力されるでしょう。各数字がきれいに1個ずつ出力されないのです。
000 001 002 : 169 169 170 170 171 171 172 172 173 173 : 999VectorがThread-safeなのは、Vectorインスタンス内部に限ります。つまりVectorインスタンスの内部ではデータの整合性は保たれますが、Vectorを使う側(上記プログラムではAクラス)では必ずしもデータを正しく操作できることを保証するものではありません。
簡単な図解を作りました。
| To use the legacy Collection is not necessarily thread-safe. |
次のように、Vectorインスタンスにアクセスする範囲全体を排他する必要があります。
public void run() {
final List<String> v = this.v;
for(int i=0; i<1000; i++){
//3桁の数字文字列を生成
final String s = createNumberString(i);
synchronized(v){
//文字列sがVectorに含まれていなければ、追加する
if(v.contains(s) == false){
v.add(s);
}
}
}
}
なお今回のようなケースではAクラスでのsynchronizedとVector内部でのsynchronizedとロックが二重になりますから、スレッドセーフではないjava.util.ArrayListを使う方が、速度性能が良くなりますね。
0 件のコメント:
コメントを投稿