优秀的编程知识分享平台

网站首页 > 技术文章 正文

learnJUC系列(三)集合的多线程并发安全问题

nanyue 2024-08-12 22:30:13 技术文章 11 ℃

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()实现:

最近发表
标签列表