1、ArrayList线程不安全
代码:
public class ContainerNotSafeTest {
public static void main(String[] args) throws InterruptedException {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 30; i++) {
int tempInt = i;
new Thread(() -> {
list.add(tempInt);
}).start();
}
TimeUnit.SECONDS.sleep(2);
System.out.println(list);
}
}
结果:
[null, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 19, 21, 22, 23, 24, 25, 26, 27, 28, 29]
代码:
创建30个线程,向ArrayList集合添加元素
public class ContainerNotSafeTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for(int i = 0; i < 30; i++){
int tempInt = i;
new Thread(()->{
list.add(tempInt);
System.out.println(list);
}).start();
}
}
}
结果:
出现ConcurrentModificationException,因为并发争抢修改,导致并发修改异常
[0, 1]
[0, 1, 2, 3, 4]
[0, 1, 2, 3]
Exception in thread "Thread-2" [0, 1]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
Exception in thread "Thread-13" [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19]Exception in thread "Thread-18"
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24]
Exception in thread "Thread-25" [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 16]
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
at java.util.ArrayList$Itr.next(ArrayList.java:861)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at com.example.ContainerNotSafeTest.lambda$main$0(ContainerNotSafeTest.java:14)
at java.lang.Thread.run(Thread.java:748)
2、Vector线程安全
把 ArrayList 改成 Vector
List<String> list = new Vector<>();
Vector、ArrayList都是以类似数组的形式存储在内存中,Vector和ArrayList提供的方法是差不多相同的,但Vector每个方法都是同步方法,添加元素的方法,多线程情况下,只允许一个线程进入,所以不会出现并发修改异常
Vector的add方法是同步方法:
// Vector.java
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
Vector的get方法是同步方法:
// Vector.java
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
3、Collections的synchronizedList方法
使用Collections的synchronizedList,得到一个SynchronizedList,Collections内部类有SynchronizedList
List<String> list = Collections.synchronizedList(new ArrayList<>());
Collections的synchronizedList方法:
// Collections.java
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
SynchronizedList 的add方法与remove方法:
// SynchronizedList.java
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
synchronized (mutex) {return list.remove(index);}
}
4、CopyOnWriteArrayList
CopyOnWriteArrayList写时复制
List<String> list = new CopyOnWriteArrayList<>();
CopyOnWrite容器即写时复制的容器,往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object[]进行复制,复制出一个新的容器Object[] newElements,然后新的容器Objet[] newElements里面添加元素,添加完元素之后,再将原容器的引用指向新的setArray(newElements);这样做的好处是可以对CopyOnWrite容器进行并发的读,是不需要加锁的,所有CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器
CopyOnWriteArrayList 的 add 方法:
// CopyOnWriteArrayList.java
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();
}
}
CopyOnWriteArrayList 的 get 方法:
// CopyOnWriteArrayList.java
public E get(int index) {
return get(getArray(), index);
}
HashSet与HashMap
对于HashSet:
Set<String> set1 = new HashSet<>(); //底层new HashMap<>()
Set<String> set2 = Collections.synchronizedSet(new HashSet<>());
Set<String> set3 = new CopyOnWriteArraySet<>(); //底层CopyOnWriteArrayList
对于HashMap:
Map<String, String> map1 = new HashMap<>();
Map<String, String> map2= Collections.synchronizedMap(new HashMap<>());
Map<String, String> map3 = new ConcurrentHashMap<>();