面试题答案
一键面试同步机制
- CopyOnWriteArrayList:采用写时复制的策略。在执行写操作(如add、set等)时,会先复制一份原数组,在新数组上进行操作,操作完成后将原数组引用指向新数组。读操作(如get)则直接读取原数组,无需加锁。这使得读操作是线程安全且无锁的,提高了读性能。
- Vector:使用synchronized关键字对几乎所有的方法(如add、get、remove等)进行同步,确保同一时间只有一个线程能访问Vector,实现线程安全。
性能
- 读性能:
- CopyOnWriteArrayList:由于读操作无锁,在高并发读场景下性能非常好。读操作不会被写操作阻塞。
- Vector:由于方法都加锁,读操作会受锁的影响,在高并发读场景下性能不如CopyOnWriteArrayList。
- 写性能:
- CopyOnWriteArrayList:写操作时要复制数组,开销较大,尤其是数组元素较多时。每次写操作都要进行数组复制和内存分配,性能较低。
- Vector:虽然写操作加锁,但相比CopyOnWriteArrayList不需要复制数组,在写操作不频繁时,性能可能优于CopyOnWriteArrayList。
适用场景
- CopyOnWriteArrayList:适用于读多写少的场景,例如配置信息的读取,数据一旦初始化很少修改,但可能被多个线程频繁读取。
- Vector:适用于读写比较均衡,或者写操作相对较多,且对数据一致性要求较高的场景。比如在一些简单的多线程数据处理场景,对性能要求不是极致高,同时需要保证数据一致性。
举例
假设我们有一个应用程序,需要维护一个全局的配置信息列表,这个列表在系统启动时初始化,之后很少修改,但在运行过程中会被大量线程频繁读取。这种情况下优先选择CopyOnWriteArrayList。
示例代码:
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class ConfigReader {
private static final List<String> configList = new CopyOnWriteArrayList<>();
static {
// 初始化配置
configList.add("config1");
configList.add("config2");
}
public static String getConfig(int index) {
return configList.get(index);
}
}
如果是一个简单的多线程任务队列,任务的添加和获取操作频率差不多,且需要严格保证数据一致性,那么选择Vector更合适。
示例代码:
import java.util.List;
import java.util.Vector;
public class TaskQueue {
private static final List<String> taskList = new Vector<>();
public static void addTask(String task) {
taskList.add(task);
}
public static String getTask() {
if (!taskList.isEmpty()) {
return taskList.remove(0);
}
return null;
}
}