面试题答案
一键面试可能出现的问题
- ConcurrentModificationException异常:
- 产生原因:当一个线程正在遍历集合(使用迭代器),而另一个线程对集合的结构进行了修改(如添加、删除元素)时,就会抛出该异常。在非线程安全的集合类(如ArrayList、HashMap等)中,迭代器依赖于集合的内部状态。当集合结构被修改后,迭代器所依赖的状态发生变化,而迭代器并没有感知到这种变化,从而抛出异常。例如,在一个线程中通过
Iterator
遍历ArrayList
,另一个线程同时调用ArrayList
的add
方法添加元素,就可能引发此异常。
- 产生原因:当一个线程正在遍历集合(使用迭代器),而另一个线程对集合的结构进行了修改(如添加、删除元素)时,就会抛出该异常。在非线程安全的集合类(如ArrayList、HashMap等)中,迭代器依赖于集合的内部状态。当集合结构被修改后,迭代器所依赖的状态发生变化,而迭代器并没有感知到这种变化,从而抛出异常。例如,在一个线程中通过
常见解决方案
- 使用并发集合类:
- ConcurrentHashMap:
- 优点:它是线程安全的哈希表。允许多个线程同时读取,并且对不同的桶进行写操作时可以并发执行,具有较高的并发性能。例如,在多线程环境下进行频繁的读操作和偶尔的写操作时,性能表现优秀。
- 缺点:相比普通的HashMap,它的实现更为复杂,占用内存可能更多。而且在迭代时,迭代器反映的是迭代器创建时集合的状态,而不是实时状态。
- CopyOnWriteArrayList:
- 优点:线程安全的List实现。在进行写操作(如添加、删除元素)时,会创建一个底层数组的副本进行修改,读操作则直接读取原数组,所以读操作不会被写操作阻塞,非常适合读多写少的场景,比如缓存数据的读取。
- 缺点:写操作时由于要复制数组,开销较大,不适合频繁写操作的场景。而且迭代器反映的是创建时的状态,不能实时反映集合的变化。
- ConcurrentHashMap:
- 采取同步机制:
- 使用Collections.synchronizedXxx方法:
- 优点:可以将非线程安全的集合类包装成线程安全的集合类,使用简单,不需要对原有代码结构进行大的改动。例如,通过
Collections.synchronizedList(new ArrayList<>())
可以将ArrayList
包装成线程安全的列表。 - 缺点:这种同步方式是对整个集合进行同步,当一个线程进行读写操作时,其他线程只能等待,并发性能较低,特别是在高并发场景下可能成为性能瓶颈。
- 优点:可以将非线程安全的集合类包装成线程安全的集合类,使用简单,不需要对原有代码结构进行大的改动。例如,通过
- 手动同步:
- 优点:可以根据实际业务需求,对集合的操作进行更细粒度的同步控制。比如只对关键的写操作进行同步,而读操作可以并发执行,提高并发性能。
- 缺点:实现较为复杂,需要开发人员对同步机制有深入的理解,否则容易出现死锁等并发问题。同时,代码的维护成本较高,因为需要手动管理同步块。
- 使用Collections.synchronizedXxx方法: