我是 ABin-阿斌:写一生代码,创一世佳话,筑一揽芳华。 如果小伙伴们觉得我的文章有点 feel ,那就点个赞再走哦。
文章目录
一、List接口
举例说明:
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//第一种解决方案:
//List<String> list = new Vector<>();
//第二种解决方案:
//List<String> list = Collections.synchronizedList(new ArrayList<>());
//第三种解决方案:(最优方案)
//List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i <= 66; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 6));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
使用 AarrayList 在高并发下出现的问题如下:
造成问题的原因:
- 由于 fail-fast 机制的存在,抛出了 java.util.ConcurrentModificationException 修改异常的错误。modcount 是 ArrayList 源码中的一个变量,用来表示修改的次数,因为 ArrayList 不是为并发情况而设计的集合类。
如何解决这个问题:
方式一:List list = new Vector<>();
- 可以使用 Vector 集合,Vector 集合是线程安全版的 ArrayList,其方法都上了一层synchronized 进行修饰,采取 jvm 内置锁来保证其并发情况下的原子性、可见性、有序性。但同时也带来了性能问题,因为 synchronized 一旦膨胀到重量级锁,存在用户态到和心态的一个转变,多线程的上下文切换会带来开销。另一个问题是 Vector 集合的扩容没有 ArrayList 的策略好
方式二:使用Collections.synchronizedList
//在数据量小的时候,可以使用
List<String> list = Collections.synchronizedList(new ArrayList<>());
方式三:采用JUC提供的并发容器,CopyOnWriteArrayList
List<String> list = new CopyOnWriteArrayList<>();
底层源码分析:
- 和 ArrayList 一样,其底层数据结构也是数组,加上 transient 不让其被序列化,加上 volatile 修饰来保证多线程下的其可见性和有序性。
- CopyOnWrite 容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器 Object[] 进行Copy (使用 Arrays.copyof() 进行扩容) ,复制出一个新的容器 Object[] newElements,然后向新的容器 Object[] newElements 里添加元素。
- 添加元素后,再将原容器的引用指向新的容器 setArray(newElements)。这样做的好处是可以对CopyOnWrite 容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
- 所以 CopyOnWrite 容器也是一种读写分离的思想,读和写不同的容器。
二、Set接口
举例说明:
public class SetTest {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
//第一种解决方案:
//Set<String> set = Collections.synchronizedSet(new HashSet<>());
//第二种解决方案:(最优方案)
//Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i <= 66; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 6));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
}
具体Set接口底层源码可点击我下方链接:
三、Map接口
举例说明:
public class MapTest {
public static void main(String[] args) {
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 1; i <=30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring( 0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}