面试题答案
一键面试可能出现的问题
- 并发修改异常(ConcurrentModificationException):当一个线程正在遍历
LinkedHashSet
,另一个线程同时对其进行修改(添加或删除元素)时,会抛出此异常。因为LinkedHashSet
本身不是线程安全的,在多线程环境下,迭代器无法正确处理集合结构的变化。 - 数据不一致:由于多个线程同时访问和修改
LinkedHashSet
,可能导致读取到的数据不是最新的,或者在修改过程中丢失更新,造成数据不一致的情况。
解决方法
- 使用同步包装器:
可以通过
Collections.synchronizedSet
方法将LinkedHashSet
包装成线程安全的集合。示例代码如下:
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
public class SynchronizedLinkedHashSetExample {
public static void main(String[] args) {
Set<String> linkedHashSet = new LinkedHashSet<>();
Set<String> synchronizedSet = Collections.synchronizedSet(linkedHashSet);
// 多线程操作 synchronizedSet
}
}
在遍历这个同步集合时,需要手动同步块来确保线程安全,例如:
synchronized (synchronizedSet) {
for (String element : synchronizedSet) {
System.out.println(element);
}
}
- 使用并发集合类:
ConcurrentSkipListSet
是 Java 提供的线程安全的有序集合类,虽然它不是LinkedHashSet
的直接替代品(ConcurrentSkipListSet
是基于跳表实现的有序集合,而LinkedHashSet
基于链表维护插入顺序),但在某些场景下可以满足多线程有序集合的需求。示例代码如下:
import java.util.concurrent.ConcurrentSkipListSet;
public class ConcurrentSkipListSetExample {
public static void main(String[] args) {
ConcurrentSkipListSet<String> concurrentSkipListSet = new ConcurrentSkipListSet<>();
// 多线程操作 concurrentSkipListSet
}
}
如果需要严格保持插入顺序,可以考虑使用 ConcurrentHashMap
结合 LinkedList
来模拟线程安全的 LinkedHashSet
。具体实现思路是用 ConcurrentHashMap
存储元素,并使用 LinkedList
维护插入顺序。
3. 使用锁机制:
可以使用 ReentrantLock
或其他锁机制来手动同步对 LinkedHashSet
的操作。示例代码如下:
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
public class LockedLinkedHashSetExample {
private static Set<String> linkedHashSet = new LinkedHashSet<>();
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
// 多线程操作 linkedHashSet
lock.lock();
try {
linkedHashSet.add("element1");
for (String element : linkedHashSet) {
System.out.println(element);
}
} finally {
lock.unlock();
}
}
}
在每次对 LinkedHashSet
进行读写操作前,先获取锁,操作完成后释放锁,以确保线程安全。