一、HasheSet 集合的不安全

  1、故障现象

public class NotSafeSetDemo {
    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, 8));
                //从集合获取内容
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }
}

  运行结果:

  

 

  2、分析

  (1)HashSet 是线程不安全的
  (2)HashSet 的底层是一个 HashMap(),但是这个 HashMap 的 value 永远是一个 Object 类型的常量(PRESENT),但HashSet的add是放一个值(调用map的PUT方法),而HashMap是放K、V键值对
public HashSet() {
    map = new HashMap<>();
}
 
private static final Object PRESENT = new Object();
 
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

    因为底层使用的是 HashMap,而 HashMap 也是线程不安全的,并且 HashSet 的 add 方法也没有使用 synchronized 同步锁。

 

二、方案一

  1、使用 Collections 工具类

  Collections 工具类也提供了线程安全的 Set 集合。

  2、代码实现

public class SafeSetDemo {

    public static void main(String[] args) {
        Set<String> set = Collections.synchronizedSet(new HashSet<>());

        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                //向集合添加内容
                set.add(UUID.randomUUID().toString().substring(0, 8));
                //从集合获取内容
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }
}

 

三、方案二

  1、使用 CopyOnWriteArraySet 

    原理同 CopyOnWriteArrayList。

  2、代码实现

public class SafeSetDemo {

    public static void main(String[] args) {

        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                //向集合添加内容
                set.add(UUID.randomUUID().toString().substring(0, 8));
                //从集合获取内容
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }
}

 

posted on 2022-01-20 22:42  格物致知_Tony  阅读(1061)  评论(0)    收藏  举报