1、java集合框架体系
集合框架部分应该是java开发极其常用的类,比如ArrayList、Hashmap、Set等,结合自己使用场景,合理使用各具特色的实现类,能够很好地提高开发效率。体系结构具体不做展开,可结合下图自行了解。本文以ArrayList为例,说明线程不安全的情况,以及解决方法。
引用自菜鸟教程Java集合框架
2、ArrayList线程不安全,原因及解决方法
关于ArrayList线程安全的讨论,一般相对应还有Vector。我们可以通过查看他们的源码实现,进行对比。
ArrayList
Vector
可以看到Vector的add()方法有加锁,从而保证了多线程并发场景时的线程安全,但也因此导致性能大大降低,因此实际开发中很少使用。
ArrayList线程不安全示例
package com.panda00hi.juc;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* 集合类多线程不安全(ArrayList为例)
*
* @author panda00hi
* @date 2022/4/9
*/
public class ContainerNotSafeDemo {
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);
}, "Thread " + i).start();
}
}
}
运行结果:
原因分析:
一个正在写入,另一个来抢夺,导致资源竞争,从而出现【并发修改异常】
解决办法:
List<String> list = new Vector<>();// 法1:Vector线程安全
List<String> list = Collections.synchronizedList(new ArrayList<>());// 法2:使用辅助类
List<String> list = new CopyOnWriteArrayList<>();// 法3:写时复制,读写分离
Map<String, String> map = new ConcurrentHashMap<>();
Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
方法1: 使用线程安全的集合类vector
方法2: Collections工具类,提供的多线程方法,对非线程安全的集合进行增强。(注意:【Collection】是接口,提供了对集合进行基本操作的通用的接口方法,意义是面向接口编程,为各种具体的集合提供最大化统一的操作方式;【Collections】是一个包装类,包含各种有关集合操作的静态方法,如排序、线程安全等,相当于一个工具类,为Collection框架提供增强)
方法2: 使用CopyOnWriteArrayList
2、CopyOnWrite写时复制
CopyOnWrite容器,即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object[]进行copy,复制出一个新的容器Object[] newElements ,新的容器里添加元素,添加完元素之后,再将原来容器的引用指向新的容器setArray(newElements); 。这样的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁。因为当前容器不会添加任何元素,所以CopyOnWrite容器是一种读写分离的思想,读和写不同的容器。
可以类比签到表场景,后到的部分,要将原有名单复制一份,原有作废,后到的中新名单末尾追加,以此类推。
CopyOnWriteArrayList源码中add()实现: