在Java ArrayList(或类似的东西)上同时读取和设置
在Java中,如果多个线程读取在Java ArrayList(或类似的东西)上同时读取和设置,java,concurrency,Java,Concurrency,在Java中,如果多个线程读取Java.util.ArrayList中的值,其中一个线程可以通过调用ArrayList.set(index,newVal)来设置一个条目,这样做行吗?(我会解释我所说的“工作”是什么意思) 假设数组列表的大小没有改变(例如,它是预定义的并用null填充)。还假设只有一个指定的线程将调用set方法 我认为它工作得很好只要 它不会抛出任何类型的异常 更新的条目最终将被读取线程看到 我想你可以这样做 List list = Collections.synchronize
Java.util.ArrayList
中的值,其中一个线程可以通过调用ArrayList.set(index,newVal)
来设置一个条目,这样做行吗?(我会解释我所说的“工作”是什么意思)
假设数组列表的大小没有改变(例如,它是预定义的并用null填充)。还假设只有一个指定的线程将调用set方法
我认为它工作得很好只要
我想你可以这样做
List list = Collections.synchronizedList(new ArrayList());
...
synchronized(list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
如果只设置值而不更改长度,那么这可能适合您使用。从 请注意,此实现不可用 同步的。如果有多个线程 访问ArrayList实例 同时,以及至少一个 线程修改列表 从结构上讲,它必须是同步的 外部。(A)结构修改 是添加或删除的任何操作 一个或多个元素,或显式地 调整支持数组的大小;仅 不允许设置元素的值 结构上的修改。) 如果您正在进行结构修改,或者多个线程正在写入ArrayList,那么您可能希望使用Collections.synchronizedList来获取线程安全列表。如果您没有进行结构修改,则使用普通数组列表给出第1点) 第2)点也可能在实践中给出,但在Java内存模型中对此没有保证。所以,你必须从外部确保这一点。可以使用一些易失性变量来保证“先发生后发生”关系,也可以对同步执行相同的操作。在
AtomicReference
(和其他AtomicXXX类)上还有一个lazySet()
方法,在这里可能会有所帮助,但我不确定我是否理解正确
1) 它不会扔任何东西
例外情况,2)更新的条目将
最终会被读线程看到
一般来说,如果数组是固定长度的(确保),并且没有添加或删除,那么答案是肯定的,它将毫无例外地工作。固定长度数组列表与长度为N的数组没有区别。集合对内部加工的所有作用类似(但不完全相同):
这当然是一个实现细节,从一个版本到另一个版本都有保证。内存可见性和顺序无法确保,但如果这不是您关心的问题,那么简单的设置不应该导致意外错误(从Java 1.6_24开始)如果不需要一致性,您可以在不锁定的情况下同时读取和设置。每次要修改数组时,创建一个新数组,修改它并将引用复制到新数组,将旧数组留给JVM
Array tempArr = new ArrayList<Object>(yourOldArr);
// modify the tempArray without changing the size
tempArr[0] = element;
yourOldArr = tempArr; // atomic operation
Array tempArr=newarraylist(yourldarr);
//修改tempArray而不更改其大小
tempArr[0]=元素;
yourldarr=tempArr;//原子操作
如果要修改tempArr的长度,可以在每次读取时返回旧数组的副本。对引用的写入和读取始终是原子的,而不管它们是以32位还是64位值实现的,因此您在那里是安全的 但是,它不能保证工作,因为不能保证即使列表长度不变,ArrayList实现也不会对get/set执行一些内务处理。此内务管理可能不是线程安全的。例如,假设您创建了一个数组列表,并调整了容量大小,从此不再更改它。您不知道阵列列表延迟了工作,仅将几个元素从旧的备份阵列移动到新的备份阵列(每次获取/放置),以便调整大小以恒定时间运行。当并发get从旧备份数组传输值0xahhhhe11覆盖新值时,集合可能会将值0xbeefbabe放入新备份数组
另外,如果足够聪明的优化器决定使用积极的内联线,并且get代码被优化掉,get可能“永远”看不到更改。问题是在特定索引处设置元素,而不是迭代列表。这两个操作都有同步方面的问题,但它们略有不同。为什么不使用一个简单的数组呢?或者只是
数组。asList
。第2点不保证。第2点在实践中不一定给出。如果线程1正在执行而(数组[0]!=1)线程睡眠(10)代码>,JVM很可能会优化while
循环,并将其替换为while(true)线程代码>,将其转换为无限循环。因此,如果线程2稍后执行:array[0]=1代码>,它永远不会被看到。我仍然不知道您的实现如何保证需求2。(请注意,还有一个CopyOnWriteArrayList,它只执行您内部的建议。)
Array tempArr = new ArrayList<Object>(yourOldArr);
// modify the tempArray without changing the size
tempArr[0] = element;
yourOldArr = tempArr; // atomic operation