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

Posted ABin-阿斌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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接口的具体介绍与使用

以上是关于JUC高级多线程_04:高并发下集合类的具体使用的主要内容,如果未能解决你的问题,请参考以下文章

JUC高级多线程_08:线程池的具体介绍与使用

JUC高级多线程_10:Future异步回调的具体介绍与使用

JUC高级多线程_09:ForkJoin框架的具体介绍与使用

HashMap在高并发下如果没有处理线程安全会有怎样的安全隐患,具体表现是什么

JUC高级多线程_11:JMM与Volatile的具体介绍与使用

JUC高级多线程_07:读写锁与阻塞队列的具体介绍与使用