为什么在并发下HashSet、HashMap是不安全的?如何解决?


Set集合不安全问题

与List一样,在多线程环境下Set集合依旧会出现线程不安全问题。

    public static void main(String[] args) {

        //解决方案 使用Collections的synchronizedSet方法
        Set<String> set = Collections.synchronizedSet(new HashSet<>());

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

HashSet的底层是什么?

我们打开HashSet的源码,可以发现,HashSet其实就是一个HashMap

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

HashSet的add()方法,其实就是map的key,key是无法重复的,PRESENT是一个常量

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

PRESENT是一个不变的值

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

HashMap不安全问题

HashMap源码阅读:

点击HashMap源码,我们可以看到HashMap默认的2个参数,initialCapacity(初始化容量)默认为16(源码中使用位运算表示)、loadFactor(影响因子)为0.75f;

    //初始化容量
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    //影响因子
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    //有参的方法
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }

HashMap多线程下不安全问题

代码示例: 演示多并发环境下HashMap出现java.util.ConcurrentModificationException并发修改异常

    public static void main(String[] args) {

        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);
            }, String.valueOf(i)).start();
        }

    }

分类:Java
标签:
文章目录