ArrayList部分源碼
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
/**
* 列表元素集合數組
* 如果新建ArrayList對象時沒有指定大小,那麼會將EMPTY_ELEMENTDATA賦值給elementData,
* 並在第一次添加元素時,將列表容量設置爲DEFAULT_CAPACITY
*/
transient Object[] elementData;
/**
* 列表大小,elementData中存儲的元素個數
*/
private int size;
}
add方法
public boolean add(E e) {
/**
* 添加一個元素時,做了如下兩步操作
* 1.判斷列表的capacity容量是否足夠,是否需要擴容
* 2.真正將元素放在列表的元素數組裏面
*/
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
ArrayList線程不安全的表現
在多個線程進行add操作時可能會導致elementData數組越界。
public static void main(String[] args) throws InterruptedException {
final List<Integer> list = new ArrayList<Integer>();
// 線程A將0-1000添加到list
new Thread(() -> {
for (int i = 0; i < 1000 ; i++) {
list.add(i);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
// 線程B將1000-2000添加到列表
new Thread(() -> {
for (int i = 1000; i < 2000 ; i++) {
list.add(i);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
Thread.sleep(1000);
// 打印所有結果
for (int i = 0; i < list.size(); i++) {
System.out.println("第" + (i + 1) + "個元素爲:" + list.get(i));
}
}
多線程情況下,一個線程正在寫入,另一個線程也在寫入,導致數據不一致異常,併發生修改異常 java.util.ConcurrentModificationException
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
for (int i =1; i<=30 ; i++) {
new Thread(() -> {
list.add("a");
list.add("b");
list.add("c");
list.add("d");
System.out.println(list.toString());
}).start();
}
}
解決辦法
//Synchronized對代碼進行加鎖,力度大,所以代碼執行效率低下
List<String> list = Collections.synchronizedList(new ArrayList<String>());
//寫時複製通過lock機制進行枷鎖
/*CopyOnWrite容器即寫時複製的容器。往一個容器添加元索的時候,不直接往當前容器Object[]添加,
而是先將當前容器Object[]進行Copy,複製出一個新的容器object[] newElements,
然後往新的容器object[] newElements 裏添加元素,
添加完元素之後,再將原容器的引用指向新的容器setArray(newElements);。
這樣做的好處是可以CopyOnWrite容器進行併發的讀,而不需要加鎖,因爲當前容器不會添加任何元素。
所以CopyOnWrite 容器也是一種讀寫分離的思想,讀和寫不同的容器*/
List<String> list = new CopyOnWriteArrayList();
List<String> list = new Vector<>();
CopyOnWriteArrayList add方法源碼
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
額外說下 ArrayList與LinkedList;這兩個都是接口List下的一個實現,用法都一樣,但用的場所的有點不同,ArrayList適合於進行大量的隨機訪問的情況下使用,LinkedList適合在表中進行插入、刪除時使用,二者都是非線程安全,解決方法同上(爲了避免線程安全,以上採取的方法,特別是第二種,其實是非常損耗性能的)