一、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(); } } }