2014-02-11 44 views
1

我有几个ArrayList<T>包含用户定义的对象(例如List<Student>, List<Teachers>)。对象本质上是不可改变的,即不提供setter,而且由于问题的本质,“没有人”会试图修改这些对象。一旦'ArrayList'被填充,不允许进一步添加/移除对象/可能。所以List不会动态改变。ArrayList可用于多线程环境中的只读目的吗?

在这样的给定条件下,这个数据结构(即ArraList)能否被多线程(同时)安全地使用?每个线程将只读取对象属性,但没有可能的“设置”操作。

所以,我的问题是我可以依靠ArrayList?否则,在这种情况下可以使用哪些其他更便宜的数据结构?

+2

如果对象不可能有来自不同线程的状态变化,那么问题是什么?也许我读错了这个问题,但它似乎是一个明智之举。唯一的问题可能是如果任何ArrayList元素的状态可以改变。我不会将列表传递给其他对象,而是将列表的深层副本或类的对象完全封装并保护列表。 –

+0

是的,你可以。尝试一下。 –

+2

您可能仍然需要某种同步来确保初始化列表的写操作发生在读操作之前,其中*发生之前*指的是[JLS中定义的概念](http://docs.oracle.com)。 com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5),否则可能会遇到缓存一致性或指令重新排序的问题,导致其他线程看到部分构建的列表。 – user2357112

回答

1

是的,你应该能够将数组传递给每个线程。只要在任何线程可能获取信息之前数组完成写入,应该没有访问错误。

2

如果线程在安全出版物之后从未修改过,则可以共享线程之间的任何对象或数据结构。如注释中所述,在初始化ArrayList的写入和其他线程获取引用的读取之间必须存在* happen-before *关系。

E.g.如果在启动其他线程之前或在将列表中的任务提交到ExecutorService之前完全设置了ArrayList,则表明您是安全的。

如果线程已经运行,则必须使用其中一个线程安全机制将ArrayList引用移交给其他线程,例如,通过把它放在BlockingQueue

即使是最简单的形式,如将参考文件存储到static finalvolatile字段也可以使用。

请记住,以后不要修改对象的前提条件必须始终成立。我们建议使用Collections.unmodifiableList(…)包装清单执行这一约束,忘了原来的列表引用发布前:

class Example { 
    public static final List<String> THREAD_SAFE_LIST; 
    static { 
    ArrayList<String> list=new ArrayList<>(); 
    // do the setup 
    THREAD_SAFE_LIST=Collections.unmodifiableList(list); 
    } 
} 

class Example { 
    public static final List<String> THREAD_SAFE_LIST 
    =Collections.unmodifiableList(Arrays.asList("foo", "bar")); 
}