2016-09-26 11 views
0

我們知道ArrayList不是線程安全的,並且VectorList都是。我想創建一個程序來顯示操作正在VectorList中同步執行,而不是在ArrayList中執行。唯一的問題,我面對的是如何?什麼樣的操作?
例如: - 如果我們爲任何一個列表添加一個值,程序只需添加值。
我試圖做一個但實現了同步我的程序依賴於變量j,而不是ArrayList或VectorList。如何顯示arraylist java的不同步性?

public class ArrayDemo implements Runnable { 

    private static ArrayList<Integer> al = new ArrayList<Integer>(); 
    Random random = new Random(); 
    int j = 0; 
    public void run() { 
     while (j < 10) { 
      int i = random.nextInt(10); 
      al.add(i); 
      System.out.println(i + " "+ Thread.currentThread().getName()); 
      j++; 
      //System.out.println(al.remove(0)); 
     } 

    } 

    public static void main(String[] args) { 
     ArrayDemo ad = new ArrayDemo(); 
     Thread t = new Thread(ad); 
     Thread t1 = new Thread(ad); 
     t.start();t1.start(); 
    } 

} 
+0

嘿,我試圖尋找它,但它一直帶我去不同的更有用的主題不是這樣基本的例子。 –

+1

如果你想測試線程(聯合國)安全,你將不得不訪問/修改多個線程相同的集合。現在您正在創建2個矢量,並使用單個線程訪問每個矢量。 – Ma3x

+0

對不起,我的錯誤讓我糾正和看到。 –

回答

1

小測試程序:

public class Test extends Thread { 
    public static void main(String[] args) throws Exception { 
     test(new Vector<>()); 
     test(new ArrayList<>()); 
     test(Collections.synchronizedList(new ArrayList<>())); 
     test(new CopyOnWriteArrayList<>()); 
    } 
    private static void test(final List<Integer> list) throws Exception { 
     System.gc(); 
     long start = System.currentTimeMillis(); 
     Thread[] threads = new Thread[10]; 
     for (int i = 0; i < threads.length; i++) 
      threads[i] = new Test(list); 
     for (Thread thread : threads) 
      thread.start(); 
     for (Thread thread : threads) 
      thread.join(); 
     long end = System.currentTimeMillis(); 
     System.out.println(list.size() + " in " + (end - start) + "ms using " + list.getClass().getSimpleName()); 
    } 
    private final List<Integer> list; 
    Test(List<Integer> list) { 
     this.list = list; 
    } 
    @Override 
    public void run() { 
     try { 
      for (int i = 0; i < 10000; i++) 
       this.list.add(i); 
     } catch (Exception e) { 
      e.printStackTrace(System.out); 
     } 
    } 
} 

樣本輸出

100000 in 16ms using Vector 
java.lang.ArrayIndexOutOfBoundsException: 466 
    at java.util.ArrayList.add(ArrayList.java:459) 
    at Test.run(Test.java:36) 
java.lang.ArrayIndexOutOfBoundsException: 465 
    at java.util.ArrayList.add(ArrayList.java:459) 
    at Test.run(Test.java:36) 
java.lang.ArrayIndexOutOfBoundsException: 10 
    at java.util.ArrayList.add(ArrayList.java:459) 
    at Test.run(Test.java:36) 
32507 in 15ms using ArrayList 
100000 in 16ms using SynchronizedRandomAccessList 
100000 in 3073ms using CopyOnWriteArrayList 

正如你所看到的,與Vector它正常完成並返回100000,這是預期的大小後添加10個並行線程中的10000個值。

隨着ArrayList你看到兩個不同的故障:

  • 三線程中調用add()死與ArrayIndexOutOfBoundsException
  • 即使三個失敗的線程立即死亡,在添加任何東西之前,其他7個線程仍應該每個都添加10000個值,總計值爲70000,但該列表僅包含32507值,因此獲得的很多附加值丟失。

第三個測試,使用Collections.synchronizedList(),就像Vector一樣工作。

第四個測試,使用併發CopyOnWriteArrayList,也會產生正確的結果,但由於過度複製,速度會更慢。但是,如果列表較小並且變化很少,但它會比同步訪問更快,但是經常讀取。
如果您需要迭代列表,這是非常好的,因爲即使VectorsynchronizedList()將與ConcurrentModificationException一起失敗,如果迭代時修改列表,而CopyOnWriteArrayList將迭代列表的快照。


出於好奇,我查了一些Deque實現過:

test(new ArrayDeque<>()); 
test(new ConcurrentLinkedDeque<>()); 
test(new LinkedBlockingDeque<>()); 

樣本輸出

34295 in 0ms using ArrayDeque 
100000 in 15ms using ConcurrentLinkedDeque 
100000 in 16ms using LinkedBlockingDeque 

正如你所看到的,不同步ArrayDeque顯示 「貶值」症狀,儘管它不會失敗並帶有例外。

兩個併發的實現,ConcurrentLinkedDequeLinkedBlockingDeque,工作良好且快速。

+0

偉大的答案,容易理解。我甚至沒有使用過你實現的一半集合。還有一件事,你能解釋一下你提到的ArrayDeque的「丟失值」症狀嗎? –

+1

@SaurabhGupta這是你還可以看到'ArrayList'的第二個子彈。我只是想回顧一下。 – Andreas

1

即使你簡單的程序,你可以表明ArrayList不使更多的循環迭代線程安全(10可能還不夠),並減少其他代碼減緩操作上ArrayList,特別是IO代碼,如System.out

我通過刪除RandomSystem.out調用修改了您的原始代碼。我在循環結尾添加了一個單獨的System.out.println,以顯示可能的成功終止。

但是,這段代碼並沒有完全運行。相反,它會引發異常。

Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: ... 

重要的是要學習這一點,即使類似的代碼可能不會遇到線程安全問題,如果時機不正確。這說明了爲什麼線程相關的錯誤很難找到,並且在實際崩潰程序之前可能潛伏在代碼中很長時間。

下面是修改代碼:

import java.util.*; 

public class ArrayDemo implements Runnable { 

    private static ArrayList<Integer> al = new ArrayList<Integer>(); 

    int j = 0; 
    public void run() { 
     while (j < 10000) { 
      al.add(new Integer(1)); 
      j++; 
     } 
     System.out.println("Array size: " + al.size()); 
    } 

    public static void main(String[] args) { 
     ArrayDemo ad = new ArrayDemo(); 
     Thread t = new Thread(ad); 
     Thread t1 = new Thread(ad); 
     t.start(); 
     t1.start(); 
    } 

}