これにも関わらず、思うような結果にならないケースがあります。
次は、検証用のサンプル(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 件のコメント:
コメントを投稿