我們在日常寫代碼的過程中,經常會使用多線程提高效率,我們在使用多線程過程中難免會出現往List集合修改數據。
下面我們來嘗試一下往ArrayList 添加數據:
public static void main(String[] args) { List<Integer> list = new ArrayList<>(); for (int i = 10000000; i >= 1; i--) { list.add(0); } System.out.println("源集合數量:"+list.size()); List<Integer> newList = new ArrayList<>(); long start = System.currentTimeMillis(); ExecutorService executor = Executors.newFixedThreadPool(100); for (Integer integer : list) { executor.submit(()->{ newList.add(integer+1); }); } executor.shutdown(); try { executor.awaitTermination(6, TimeUnit.MINUTES); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("時間:"+(end-start)+"ms"); System.out.println("新集合數量:"+newList.size());
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
我們使用線程池給 ArrayList 添加一千萬個元素。來看下結果:
會發現新List’的數據會少於一千萬,這是為什么呢?
因為 ArrayList 不是線程安全的,在高並發情況下對list進行數據添加會出現數據丟失的情況。
並且一個線程在遍歷List,另一個線程修改List,會報ConcurrentModificationException(並發修改異常)錯誤
那么如果我們確實需要 並發對數據進行操作,並且對結果進行收集處理,應該怎么做呢?
一,使用Vector
從源碼介紹里面我們可以看出 Viector是線程安全的,但后面也說明了,如果對線程安全沒有要求,建議使用ArrayList,因為ArrayList單分效率更高。
從源碼里面可以看到:
/** * Sets the size of this vector. If the new size is greater than the * current size, new {@code null} items are added to the end of * the vector. If the new size is less than the current size, all * components at index {@code newSize} and greater are discarded. * * @param newSize the new size of this vector * @throws ArrayIndexOutOfBoundsException if the new size is negative */ public synchronized void setSize(int newSize) { modCount++; if (newSize > elementCount) { ensureCapacityHelper(newSize); } else { for (int i = newSize ; i < elementCount ; i++) { elementData[i] = null; } } elementCount = newSize; } /** * Returns the current capacity of this vector. * * @return the current capacity (the length of its internal * data array, kept in the field {@code elementData} * of this vector) */ public synchronized int capacity() { return elementData.length; } /** * Returns the number of components in this vector. * * @return the number of components in this vector */ public synchronized int size() { return elementCount; } /** * Tests if this vector has no components. * * @return {@code true} if and only if this vector has * no components, that is, its size is zero; * {@code false} otherwise. */ public synchronized boolean isEmpty() { return elementCount == 0; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
Vector里面的操作方法,都加上了synchronized 關鍵字。下面來使用Vector走一遍代碼:
public static void main(String[] args) { List<Integer> list = new ArrayList<>(); for (int i = 10000000; i >= 1; i--) { list.add(0); } System.out.println("源集合數量:"+list.size()); List<Integer> newVector = new Vector<>(); long start = System.currentTimeMillis(); ExecutorService executor = Executors.newFixedThreadPool(100); for (Integer integer : list) { executor.submit(()->{ newVector.add(integer+1); }); } executor.shutdown(); try { executor.awaitTermination(6, TimeUnit.MINUTES); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("時間:"+(end-start)+"ms"); System.out.println("newVector數量:"+newVector.size()); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
看下結果:
我們可以發現現在,新Vector里面的數量正好是一千萬個。但是時間上要長於ArrayList。
二、使用Collections.synchronizedList()進行包裝
public static void main(String[] args) { List<Integer> list = new ArrayList<>(); for (int i = 10000000; i >= 1; i--) { list.add(0); } System.out.println("源集合數量:"+list.size()); /** * Collections.synchronizedList()包裝 */ List<Integer> newCollList = Collections.synchronizedList(new ArrayList<>()); long start = System.currentTimeMillis(); ExecutorService executor = Executors.newFixedThreadPool(100); for (Integer integer : list) { executor.submit(()->{ newCollList.add(integer+1); }); } executor.shutdown(); try { executor.awaitTermination(6, TimeUnit.MINUTES); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("時間:"+(end-start)+"ms"); System.out.println("newCollList新集合數量:"+newCollList.size()); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
結果:
我們可以發現也是一千萬條。時間上和Vector差距不大,因給給ArrayList進行了包裝以后等於是給ArrayList里面所有的方法都加上了 synchronized,和Vector實現效果差不多。
總結:在並發給List進行修改時,可以使用Vector或者Collections.synchronizedList(),不要直接使用ArrayList,在非並發情況下盡量使用ArrayList;