Set不安全
HashSet底层使用的是什么?
我们去查看源码,点击进去一看发现
其实底层就是使用了HashMap去实现的。
为什么Set不安全?
先给出代码
public class TestSet {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
for (int i = 0; i < 20; i++) {
int finalI = i;
new Thread(() -> {
set.add(String.valueOf(finalI));
System.out.println( set);
}, String.valueOf(i)).start();
}
}
}
发现在多线程下会出现ConcurrentModificationException问题。
原因:
HashSet 是基于 HashMap 实现的,它本身并没有提供内置的线程安全机制。当多个线程同时对 HashSet 进行修改时(如执行 add() 操作),可能会导致 数据丢失、数据不一致,甚至出现 ConcurrentModificationException 异常。
解决方案
1.显示加锁
Set<String> set = new HashSet<>();
Object lock = new Object();
for (int i = 0; i < 20; i++) {
int finalI = i;
new Thread(() -> {
synchronized (lock) {
set.add(String.valueOf(finalI));
System.out.println(set);
}
}, String.valueOf(i)).start();
}
2. 使用 Collections.synchronizedSet()
Set<String> set = Collections.synchronizedSet(new HashSet<>());
这样,所有对 set 的操作都将通过同步进行,从而避免了线程安全问题。然而,使用这种方式时需要注意,如果你要迭代 set,仍然需要手动加锁,因为 synchronizedSet 仅保证基本的操作是同步的。
synchronized(set) {
for (String item : set) {
// 迭代操作
}
}
3. 使用 CopyOnWriteArraySet
CopyOnWriteArraySet 是一个线程安全的 Set 实现,底层使用 CopyOnWriteArrayList。它适用于 读多写少 的场景,因为每次写操作都需要复制底层数据结构。但它在写操作频繁的场景下性能较差。
Set<String> set = new CopyOnWriteArraySet<>();
CopyOnWriteArraySet原理和CopyOnWriteArrayList类似;