1. list不安全
public class NotSafeDemo {
public static void main(String[] args) {
List<String> list =new ArrayList<>();
for (int i=0;i<=30;i++){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
以上代码报错:ConcurrentModificationException
因为list是多线程不安全的。
解决方法1:可以用Vector,
List<String> list =new Vector<>();
因为Vector的add方法:
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
使用Vector效率变差
解决方法2:Collections.synchronizedList
List<String> list = Collections.synchronizedList(new ArrayList<>());
牺牲了数据一致性
解决方法3:CopyonWriteArrayList
List<String> list = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList<>的源码如下:
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();
}
}
其添加一个元素的过程如下:
CopyOnWrite 容器即写时赋值容器,往一个容器添加元素时,不直接往当前容器Object[]中加,而是对当前容器object[]进行Copy,复制出一个新的容器Object[] newElements,往新容器里添加元素,添加完毕后,再将原容器的引用指向新的容器setArray(newElemets);好处是可以对CopyOnWrite容器进行并发的读,而不用加锁。
2. set不安全
解决思路和list一样,一是Collections.synchronizedList,二是CopyOnWriteArraySet<>();
public class NotSafeDemo2 {
public static void main(String[] args) {
Set<String> set = new CopyOnWriteArraySet<>();
for (int i=0;i<=30;i++){
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
题外话:HashSet底层是HashMap,为什么Map放<key,value>键值对(两个),但是HashSet只放一个?
因为Set 的add调用的是Map的put方法,Key值就是Set的元素值,value值是PRESENT常量
3. map不安全
用ConcurrentHashMap<>()
public class NotSafeDemo3 {
public static void main(String[] args) {
Map<String,String> map = new ConcurrentHashMap<>();
for (int i=0;i<=30;i++){
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}