在Java ArrayList(或类似的东西)上同时读取和设置

在Java ArrayList(或类似的东西)上同时读取和设置,java,concurrency,Java,Concurrency,在Java中,如果多个线程读取Java.util.ArrayList中的值,其中一个线程可以通过调用ArrayList.set(index,newVal)来设置一个条目,这样做行吗?(我会解释我所说的“工作”是什么意思) 假设数组列表的大小没有改变(例如,它是预定义的并用null填充)。还假设只有一个指定的线程将调用set方法 我认为它工作得很好只要 它不会抛出任何类型的异常 更新的条目最终将被读取线程看到 我想你可以这样做 List list = Collections.synchronize

在Java中,如果多个线程读取
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