首先需要了解什么是線程安全:線程安全就是說多線程訪問同一代碼(對象、變量等),不會產生不確定的結果。 既然說ArrayList是線程不安全的,那么在多線程中操作一個ArrayList對象,則會出現不確定的結果。具體是怎樣不確定,請看測試下面這段代碼(在此測試ArrayList的add方法):
public class ArrayListInThread implements Runnable{
//線程不安全
private List threadList = new ArrayList();
//線程安全
//private List threadList = Collections.synchronizedList(new ArrayList());
@Override
public void run() {
try {
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
//把當前線程名稱加入list中
threadList.add(Thread.currentThread().getName());
}
public static void main(String[] args) throws InterruptedException{
ArrayListInThread listThread = new ArrayListInThread();
for(int i = 0; i < 100; i++){
Thread thread = new Thread(listThread, String.valueOf(i));
thread.start();
}
//等待子線程執行完
Thread.sleep(2000);
System.out.println(listThread.threadList.size());
//輸出list中的值
for(int i = 0; i < listThread.threadList.size(); i++){
if(listThread.threadList.get(i) == null){
System.out.println();;
}
System.out.print(listThread.threadList.get(i) + " ");
}
}
}
執行幾次會發現結果不一樣,甚至有時會出現ArrayIndexOutOfBoundsException,貼出幾次執行的結果:
結果一:
結果二:
結果三:
以上執行結果說明ArrayList確實是線程不安全的,然后我們從線程並發的角度分析,為何會出現這樣的結果:
首先,我們先來看一下ArrayList中的add方法是如何實現的
//添加元素e
public boolean add(E e) {
// 確定ArrayList的容量大小
ensureCapacity(size + 1); // Increments modCount!!
// 添加e到ArrayList中
elementData[size++] = e;
return true;
}
// 確定ArrarList的容量。
// 若ArrayList的容量不足以容納當前的全部元素,設置 新的容量=“(原始容量x3)/2 + 1”
public void ensureCapacity(int minCapacity) {
// 將“修改統計數”+1,該變量主要是用來實現fail-fast機制的
modCount++;
int oldCapacity = elementData.length;
// 若當前容量不足以容納當前的元素個數,設置 新的容量=“(原始容量x3)/2 + 1”
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
//如果還不夠,則直接將minCapacity設置為當前容量
if (newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
結果中,有的值沒有出現(結果一中3沒有出現),有的出現了null值,這是由於賦值時出現了覆蓋。賦值語句為:elementData[size++] = e,這條語句可拆分為兩條:
1. elementData[size] = e;
2. size ++;
假設A線程執行完第一條語句時,CPU暫停執行A線程轉而去執行B線程,此時ArrayList的size並沒有加一,這時在ArrayList中B線程就會覆蓋掉A線程賦的值,而此時,A線程和B線程先后執行size++,便會出現值為null的情況;至於結果三中出現的ArrayIndexOutOfBoundsException異常,
則是A線程在執行ensureCapacity(size+1)后沒有繼續執行,此時恰好minCapacity等於oldCapacity,B線程再去執行,同樣由於minCapacity等於oldCapacity,ArrayList並沒有增加長度,B線程可以繼續執行賦值(elementData[size] = e)並size ++也執行了,此時,CPU又去執行A線程的賦值操作,由於size值加了1,size值大於了ArrayList的最大長度,
因此便出現了ArrayIndexOutOfBoundsException異常。
既然ArrayList是線程不安全的,但如果需要在多線程中使用,可以采用list<Object> list =Collections.synchronizedList(new ArrayList<Object>)來創建一個ArrayList對象。
原文:https://blog.csdn.net/zhangxin961304090/article/details/46804065