8

我有一个字符串(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;
            }
        }));
    }
}
4

3 回答 3

9

这完全取决于底层列表在读取操作上是否是线程安全的。Unmodifiable list 只是将所有读取调用(例如size ()get (int)等)传递给底层列表,而无需额外同步。

例如,想象一下 List 的实现,它缓存哈希码,而不是每次需要时都计算它。对于这样的实现,hashCode ()方法实际上不是只读的,因为它可能会修改内部缓存的哈希码值。

另一个例子是 LinkedList 的一种风格,它缓存对最后访问条目的引用及其索引,因此进一步尝试访问附近元素将执行得更快。对于这样的实现,方法get (int)将不是只读的,因为它更新缓存的引用和索引,因此可能不是线程安全的。

于 2013-02-05T18:01:17.657 回答
2

它是线程安全的(因为它不能被修改)。它传递相同的副本。

但是,包装的 List ( tagList) 仍然不是线程安全的。您不能在共享列表时修改包装的列表。返回的列表是安全的唯一原因unmodifiableList()是因为不允许通过它对包装的列表进行修改。

于 2013-02-05T18:00:19.890 回答
0

正如您所说,该列表是不可修改的;因此它将是线程安全的。

当你传递一个对象时,你实际上传递了对该对象的引用(而不是实际的对象)。由于只有一个副本并且它没有被修改,因此它将保持线程安全。

只有一个警告;永远不要直接访问tagList。始终通过名为tList的已包装不可修改集合访问它。如果您正确封装它,就可以实现这一点。

于 2013-02-05T18:03:32.863 回答