5.并发下不安全的集合类如何解决?

感谢秦疆老师的JUC并发编程视频,更多了解哔哩哔哩搜索【狂神说Java】。

本文内容源于秦疆老师的JUC并发编程视频教程。给狂神推荐,点赞吧!

List 不安全

package demo4;

import java.util.ArrayList;
import java.util.UUID;

//java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

java.util.ConcurrentModificationException 并发修改异常

在这里插入图片描述

  • 并发下ArraryList 不安全 扩容机制

在这里插入图片描述

解决方案: synchronized ()?

  1. Vector
List<String> list = new Vector<>();

Vector底层 add:

在这里插入图片描述
2. Collections.synchronizedList()

List<String> list = Collections.synchronizedList(new ArrayList<>())
  1. CopyOnWriteArrayList
//CopyOnWrite 写入时复制, COW 计算机程序设计领域的一种优化策略;
//多个线程调用的时候,list ,读取的时候,固定的,写入(覆盖)
//在写入的时候避免覆盖 ,造成数据问题! 
//读写分离

List<String> list = new CopyOnWriteArrayList<>();

CopyOnWriteArrayList 比 Vector牛逼在哪?CopyOnWriteArrayList 底层源码:Lock锁 ,synchronized 效率低

在这里插入图片描述

  1. 先会用,2. 货比三家,寻找其他的解决方案 3. 分析源码

Set不安全

在这里插入图片描述

package demo4;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

public class TestSet {
    public static void main(String[] args) {

        Set<String> set = new HashSet<>();

        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }

    }
}

解决方案:

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

HashSet的底层是什么?

set的本质就是map的key 是无法重复的

public HashSet() {
    map = new HashMap<>();
}


//add set的本质就是map的key 是无法重复的
public boolean add(E e) {
   return map.put(e, PRESENT)==null;
}

//PRESENT 一个不变的值
private static final Object PRESENT = new Object();

Map不安全

回顾HashMap的基本操作

加载因子 初始化容量

在这里插入图片描述

//map 是这样用的嘛? 不是工作中不用HashMap?反正我用过,我也不晓得
//默认等价于什么?   Map<String,String> map =  new HashMap<>(16, (float) 0.75);
 Map<String,String> map = new HashMap<>();

HashMap不安全

HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。

在这里插入图片描述

package demo4;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class MapTest {
    public static void main(String[] args) {

        //map 是这样用的嘛 不是?工作中不用HashMap?
        //默认等价于什么?new HashMap<>(16, (float) 0.75);
        Map<String, String> map = new HashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                System.out.println(map);
            }).start();
        }
    }
}

解决方案:

  1. 使用线程安全的HashTable (效率低)
  2. ConcurrentHashMap的锁分段技术

ConcurrentHashMap

Map<String, String> map = new ConcurrentHashMap<>();

参考链接

posted @ 2020-05-24 01:00  我有满天星辰  阅读(3)  评论(0编辑  收藏  举报