ArrayListとCopyOnWriteArrayListスレッドセキュリティテスト
3565 ワード
ArrayListは非スレッドセキュリティであり、CopyOnWriteArrayListはスレッドセキュリティであり、読み取り操作時にロックされていないArrayListであり、同時アクセスに適している.集合要素数が10000、スレッド数が100の場合に性能テストを行い、要素数とスレッド数の増加に伴い、CopyOnWriteArrayListは要素の増加と削除時の性能低下が著しく、ArrayListよりも性能が低下する.しかし、要素を検索する点ではスレッド数が増えるにつれて、ArrayListよりもパフォーマンスが向上します.
したがって、読み書きが少ない同時シーンでは、CopyOnWriteArrayListはArrayListよりも良い選択である.
次の2つの方法のスレッドを安全にテストします.
テストの結果は次のとおりです.
unsafe/safe: 9992/10000 unsafe/safe: 9996/10000 unsafe/safe: 9990/10000 unsafe/safe: 9992/10000 unsafe/safe: 9997/10000 unsafe/safe: 9997/10000 unsafe/safe: 9993/10000 unsafe/safe: 9995/10000 unsafe/safe: 9993/10000 unsafe/safe: 9995/10000
不安全なスレッドlist数が10000未満になる理由は、主に2つあります.
まずArrayListのソースコードを見てみましょう.
public boolean add(E e) { ensureCapacity(size + 1);//Increments modCount!! elementData[size++] = e; return true; }
主な問題はsize++です.この方法はスレッドが安全ではないため、次の2つの状況が発生する可能性があります.
1)スレッドAはスレッドBと同様にsize=10を取得する、同じ位置10に2回値を挿入した後、いずれもsize++を実行した結果、sizeは12となり、次にスレッドが入ると12の位置で値を挿入し続けるが、11はnullとなり、実験ではやはりそうであったが、list 1の理由は説明できない.sizeの値が減少し、リストにnullの値がある理由しか説明できません.
2)size++この操作は同様にスレッドが安全ではなく、2つのステップに分けられている.第1に、sizeの位置を取り、第2に、sizeの位置を+1とする.これにより、AスレッドとBスレッドが同時にsizeの位置を取り、+1となる可能性がある.これにより、A、Bスレッドがsize++を実行した後、sizeの値は12ではなく11となるため、異なるスレッドが同時に1つの位置に値を付与し、listの数が不足する.
したがって、読み書きが少ない同時シーンでは、CopyOnWriteArrayListはArrayListよりも良い選択である.
次の2つの方法のスレッドを安全にテストします.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class ThreadSafeDemo {
public static int demo(final List list, final int testCount) throws InterruptedException {
ThreadGroup group = new ThreadGroup(list.getClass().getName() + "@" + list.hashCode());
final Random rand = new Random();
Runnable listAppender = new Runnable() { // Runnable , run
public void run() {
try {
Thread.sleep(rand.nextInt(2));
} catch (InterruptedException e) {
return;
}
list.add("0");
}
};
for (int i = 0; i < testCount; i++) {
new Thread(group, listAppender, "InsertList-" + i).start();
//java.lang.Thread
.Thread(ThreadGroup
group, Runnable
target, String
name)
}
while (group.activeCount() > 0) {
Thread.sleep(10);
}
return list.size();
}
public static void main(String[] args) throws InterruptedException {
List unsafeList = new ArrayList();
List safeList = Collections.synchronizedList(new ArrayList()); // new CopyToWriteArrayList
final int N = 10000;
for (int i = 0; i < 10; i++) {
unsafeList.clear();
safeList.clear();
int unsafeSize = demo(unsafeList, N);
int safeSize = demo(safeList, N);
System.out.println("unsafe/safe: " + unsafeSize + "/" + safeSize);
}
}
}
テストの結果は次のとおりです.
unsafe/safe: 9992/10000 unsafe/safe: 9996/10000 unsafe/safe: 9990/10000 unsafe/safe: 9992/10000 unsafe/safe: 9997/10000 unsafe/safe: 9997/10000 unsafe/safe: 9993/10000 unsafe/safe: 9995/10000 unsafe/safe: 9993/10000 unsafe/safe: 9995/10000
不安全なスレッドlist数が10000未満になる理由は、主に2つあります.
まずArrayListのソースコードを見てみましょう.
public boolean add(E e) { ensureCapacity(size + 1);//Increments modCount!! elementData[size++] = e; return true; }
主な問題はsize++です.この方法はスレッドが安全ではないため、次の2つの状況が発生する可能性があります.
1)スレッドAはスレッドBと同様にsize=10を取得する、同じ位置10に2回値を挿入した後、いずれもsize++を実行した結果、sizeは12となり、次にスレッドが入ると12の位置で値を挿入し続けるが、11はnullとなり、実験ではやはりそうであったが、list 1の理由は説明できない.sizeの値が減少し、リストにnullの値がある理由しか説明できません.
2)size++この操作は同様にスレッドが安全ではなく、2つのステップに分けられている.第1に、sizeの位置を取り、第2に、sizeの位置を+1とする.これにより、AスレッドとBスレッドが同時にsizeの位置を取り、+1となる可能性がある.これにより、A、Bスレッドがsize++を実行した後、sizeの値は12ではなく11となるため、異なるスレッドが同時に1つの位置に値を付与し、listの数が不足する.