一、线程不安全集合在多线程操作下会出现的问题
由于ArrayList是线程不安全的,所以以ArrayList为例演示出现错误:
/**
* @author wannengqingnian
*/
public class TestArrays {
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);
}).start();
}
}
}
运行出现:
原因:
由于 ArrayList 的 add() 方法没有加锁,多个线程同时添加数据会出现 java.util.ConcurrentModificationException 异常(并发修改异常)。
二、解决ArrayList线程不安全问题方法
1、使用new Vector<>()
Vector和ArrayList的区别是vector在add()方法上加上了synchronized关键字来保证线程安全
2、使用Collections集合工具类的Collections.SynchronizeList(new ArrayList<>());
3、使用new CopyOnWriteArrayList<> ()
三、浅解解决ArrayList线程不安全的第三种方式:CopyOnWriteArrayList
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) {
synchronized (lock) {
Object[] es = getArray();
int len = es.length;
es = Arrays.copyOf(es, len + 1);
es[len] = e;
setArray(es);
return true;
}
}
CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前object[]进行Copy,复制出一个新的容器Object[] newElements,然后新的容器Object[] newElements里添加元素,添加完元素之后,再将原容器的引用指向新的容setArray(newElements);这样做的好处是可以对copyonwrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以copyonwrite容器也是一种读写分离的思想,读和写不同的容器。
四、HashSet线程不安全的解决
1、使用Collections集合工具类的Collections.SynchronizeList(new HashSet<>());
2、使用new CopyOnWriteHashSet<> ()
HashSet底层使用HashMap,HashSet保存数据的时候是一个值,而HashMap则是键值对。HashSet把值存到key,value则是一个常量。
五、HashMap线程不安全解决
1、使用ConcurrentHashMap<>()
2、使用Collections.synchronizedMap()
六、尾巴
解决集合线程安全问题,一般先看有没有线程安全可以替代的,没有看集合工具类中有没有提供可以保证线程安全的工具,还有CopyOnWrite中保证安全的集合。