JUC高级多线程_04:高并发下集合类的具体使用

我是 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 修改异常的错误。modcountArrayList 源码中的一个变量,用来表示修改的次数,因为 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接口底层源码可点击我下方链接:

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(); 
		} 
	} 
}
具体Map接口底层源码可点击我下方链接:

Map接口的具体介绍与使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值