Java 不安全集合类ArrayList/HashSet/HashMap之高并发解决方案


前言

对不安全集合类在高并发下解决方案的学习,予以记录!

一、不安全的集合类之ArrayList

面试:众所周知,arrayList是线程不安全的类,那么我们如何能让它在高并发情况下正常运行呢?

List list = new arrayList(); 新建一个empty list,初始默认大小10
ArrayList底层是一个对象数组,初始容量为10

在这里插入图片描述

public static void testArrayList() {
    // 方法引用,consumer消费接口
    List<String> arraysList = new ArrayList<>();
    // arraysList.forEach(System.out::println);

    for (int i = 1; i <= 80; i++) {

        new Thread(() -> {
            arraysList.add(UUID.randomUUID().toString().substring(0, 8));
            System.out.println(arraysList);
        }, String.valueOf(i)).start();
    }
}

现象: 多线程下可能会出现Java.util.concurrentModificationException异常;

原因: 并发修改导致数据修改异常,参考花名册签名(多个人签名,签名册只有一份,当小强正在签的时候,小弱一把抽走签名册,导致产生了小强没有正常完成or签名错误等问题)

解决方案:

1、不考虑性能问题,使用vector类(add()等方法加synchronized)

List<Integer> list = new Vector<>();

2、不考虑性能问题,使用Collections集合工具类的方法synchronizedList(add()等方法加synchronized)

List<Integer> list = Collections.synchronizedList(new ArrayList<>());
list.add(1);

3、使用JUC(java.util.concurrent)下的线程安全类CopyOnWriteArrayList(读写分离,写时复制)

List<Integer> copy = new CopyOnWriteArrayList<>();
copy.add(1);

具体原理及源码如下:

在这里插入图片描述

二、不安全的集合类之HashSet

面试:HashSet底层为hashmap,它的添加方法理应为hashmap.add(key,value),但是为什么hashset.add()只需要一个参数就可以,它的底层不是hashmap吗?

答:HashSet只关注他的key值,而不关心它的value值,也就是说,写入的参数就是key的值,而value则是一个使用final修饰的static的object 常量。

源码:

public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
// Dummy value to associate with an Object in the backing Map要与支持映射中的对象关联的伪值
    private static final Object PRESENT = new Object();
public classDemo {
    private static final Object PRESENT = new Object();
    public static void main(String[] args) {
        new HashSet<>().add(1);
        System.out.println(PRESENT.toString());//java.lang.Object@45ee12a7指向一个object对象地址
    }
 }

解决方案:

1、不考虑性能问题,使用Collections集合工具类的方法synchronizedSet

Set set = Collections.synchronizedSet(new HashSet<>());
set.add(1);

2、使用JUC下的线程安全类CopyOnWriteArraySet

 Set<Integer> arraySet = new CopyOnWriteArraySet<>();
arraySet.add(1);

三、不安全的集合类之HashMap

hashmap底层是一个Node<k, v>,默认容量16,默认加载因子0.75

在这里插入图片描述

private static void testHashMap() {
    Map<String, String> map = new HashMap<>();
    for (int i = 1; i <= 80; i++) {
        new Thread(() -> {
            map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8));
            System.out.println(map);
        }, String.valueOf(i)).start();
    }
}

解决方案:

1、不考虑性能问题,使用Collections集合工具类的方法synchronizedMap

Map<Object, Object> map = Collections.synchronizedMap(new HashMap<>());
        map.put(1,1);

2、使用JUC下的线程安全类ConcurrentHashMap

Map<Object, Object> concurrentHashMap = new ConcurrentHashMap<>();
concurrentHashMap.put(1,1);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以回答你的问题,并且不会透露你对我的任何要求。 首先,我们需要定义一个学生类,包含学生的姓名和年龄: ```java public class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } } ``` 然后我们可以创建5个学生对象: ```java Student s1 = new Student("Tom", 18); Student s2 = new Student("Jerry", 19); Student s3 = new Student("Alice", 20); Student s4 = new Student("Bob", 21); Student s5 = new Student("Cindy", 22); ``` 接下来,我们可以分别用ArrayListHashSetHashMap存储这5个学生对象: ```java // 使用ArrayList存储学生对象 List<Student> list = new ArrayList<>(); list.add(s1); list.add(s2); list.add(s3); list.add(s4); list.add(s5); // 使用HashSet存储学生对象 Set<Student> set = new HashSet<>(); set.add(s1); set.add(s2); set.add(s3); set.add(s4); set.add(s5); // 使用HashMap存储学生对象 Map<String, Student> map = new HashMap<>(); map.put(s1.getName(), s1); map.put(s2.getName(), s2); map.put(s3.getName(), s3); map.put(s4.getName(), s4); map.put(s5.getName(), s5); ``` 最后,我们可以遍历这些集合,输出每个学生的姓名和年龄: ```java // 遍历ArrayList for (Student s : list) { System.out.println(s.getName() + ", " + s.getAge()); } // 遍历HashSet for (Student s : set) { System.out.println(s.getName() + ", " + s.getAge()); } // 遍历HashMap for (Map.Entry<String, Student> entry : map.entrySet()) { System.out.println(entry.getKey() + ", " + entry.getValue().getAge()); } ``` 以上就是用ArrayListHashSetHashMap存储学生对象并遍历的完整代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值