我有一个字符串(tagList)列表,需要在多个线程之间共享以进行读取,所以我创建了一个不可修改的版本并将其传递给线程,我不确定它是否是线程安全的,因为线程只读取该列表所以我想它应该没问题?
当我将该不可修改的列表传递给线程时,它是否传递单个副本并由线程共享,还是创建多个副本并将一个副本传递给每个线程?
这是我的代码:
final List<String> tList = Collections.unmodifiableList(tagList);
List<Future<Void>> calls = new ArrayList<Future<Void>>();
FileStatus[] fsta = _fileSystem.listStatus(p);
for (FileStatus sta : fsta) {
final Path path = new Path(sta.getPath(), "data.txt");
if (!_fileSystem.exists(path)) {
continue;
}
else {
calls.add(_exec.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
filterData(path, tList);
return null;
}
}));
}
}
答案 0 :(得分:7)
这完全取决于底层列表在读取操作上是否是线程安全的。不可修改的列表只会将所有读取调用(例如size ()
,get (int)
等)传递给基础列表,而无需额外的同步。
想象一下,例如,List的实现缓存哈希码而不是每次需要时计算它。对于这样的实现,hashCode ()
方法实际上不是只读的,因为它可以修改内部缓存的哈希码值。
另一个例子是LinkedList的风格,它将对最后访问的条目及其索引的引用进行高速缓存,因此进一步尝试访问附近的元素将更快地执行。对于这样的实现,方法get (int)
将不是只读的,因为它更新了缓存的引用和索引,因此可能不是线程安全的。
答案 1 :(得分:2)
它是线程安全的(因为它不能被修改)。它传递相同的副本。
但是,包装列表(tagList
)仍然不是线程安全的。在共享包含列表时,您不得修改它。 unmodifiableList()
返回的列表唯一的原因是安全,因为不允许修改它到包装列表。
答案 2 :(得分:0)
正如你所说,列表是不可修改的;因此它将是线程安全的。
传递对象时,实际将引用传递给对象(而不是实际对象)。由于存在单个副本且未进行修改,因此它将保持线程安全。
只需一个小心;永远不要直接访问 tagList 。始终通过名为 tList 的包装不可修改集合访问它。如果正确封装它,可以实现这一点。