【问题标题】:Array In multithread environment . read and Write both数组在多线程环境中。读和写两者
【发布时间】:2013-08-02 15:34:59
【问题描述】:

我有一个长度为 100 的数组。我需要在多线程环境中使用它。共有 10 个线程访问数组。可能有两个线程 t1t2 想要一次写入同一个索引。

Object[] data = new Object[100];

实现这一点的最佳方法是什么。

解决方案 1: 只有一个线程可以写入数组。即使t1t2 线程想要写入不同的索引,也必须等待。甚至我们可以使用arrayList 并且可以使用Collections.synchronizedList(....)。

  public class ThreadSafeArray(){
    private Object[] data = new Object[100];

    public synchronized Object getValueAtIndex(int index){
       return data[index]; // Removing index range check for simple explanation
    }

    public synchronized void setValueAtIndex(int index , Object value){
      data[index] = value; // Removing index range check for simple explanation

    }
  }

解决方案 2: 两个不同的线程可以同时在两个不同的索引上写入。

        public class ThreadSafeArray(){
    private Object[] data = new Object[100];
    private Object[] lock = new Object[100];

    public Object getValueAtIndex(int index){
       synchronized(lock[index]) 
       {  
          return data[index]; // Removing index range check for simple explanation
       }
    }

    public void setValueAtIndex(int index , Object value){
       synchronized(lock[index]) 
      {
           data[index] = value; // Removing index range check for simple explanation
      } 
    }
  }

有没有更好的方法来实现这个要求?

【问题讨论】:

  • 在 JAVA 中使用 VectorCopyOnWriteArrayList 会更好
  • 你这样做是为了锻炼还是在重新发明轮子。这个问题也更适合codereview.stackexchange.com
  • @NarendraPathai 练习
  • @NarendraPathai 与解决方案 2 相比,CopyOnWriteArrayList 将是一个很好的解决方案。
  • “可能有两个线程 t1 和 t2 想同时写入同一个索引。” - 好的,那么谁应该是赢家?您需要额外的业务逻辑。

标签: java arrays multithreading thread-safety


【解决方案1】:

首先让我们讨论一下,如果我们在多个线程之间共享任何数据,无论是 Java/C#,我们会遇到什么问题。 我们需要解决三个问题。

1. **Atomicity** of read/write operation on that datastructure
2. **Visibility** changes by one thread are visible to other thread.
3. **Reordering** - compiler n processor are free to reorder these instruction 
    as long as it maintains program order for single thread execution.

现在对于您的问题,我看到的是 . 你有一个固定大小的数组,你在多个线程之间共享,你只是在设置和获取值。

首先引用分配是原子的 因此,您的以下方法是原子的。我不会说它是线程安全的。因为它仍然缺乏 能见度保证。

public void setValueAtIndex(int index , Object value){
      data[index] = value; // Removing index range check for simple explanation
    }

现在为了保证可见性,我们可以改变我们的方法(以防您读取的次数超过写入次数)

首先让你的数组声明为 volatile

volatile Object [] data = new Object[100];

现在您的 get 方法在没有同步关键字的情况下也可以正常工作

public Object getValueAtIndex(int index){
       return data[index]; // Removing index range check for simple explanation
}

上述方法将是线程安全的 现在对于 set 方法,您可能需要复制数组更改值,然后再次为其重新分配数据,即

public void setValueAtIndex(int index , Object value){
      Object tempdata =  copy(data); // make a copy of that array
      //change in the copied array 
       tempdata[index] = value; 
// reassign the array back to original array
 data = tempData;   
}

使用上述方法,您将在写入数组的成本上提高读取数组的性能。 如果您有固定长度的数组,则不需要同步,否则您需要锁定以进行变异操作

【讨论】:

  • 所有读取都是易失性的,请参阅数据声明为易失性,并且在任何突变时,复制的数组再次分配给数据,即 volatle write 。这将解决可见性问题。
  • 当一个数组被声明为易失性时,所有通过它的读取都是易失性读取
【解决方案2】:

如果您有很多线程尝试写入大量单元格,则第二种解决方案会更好,然后第一种方法可能会产生比第二种解决方案更薄的瓶颈。

在给定的 10 个线程和 100 个单元的情况下,现代计算机没有太大区别

【讨论】:

    【解决方案3】:

    我会选择解决方案 1:

    我认为它完全符合您的需求。

    我认为性能问题可以忽略。

    synchronize 
    

    您的访问权限完成了这项工作。为什么要让它比现在更难。

    提示:不会使用数组。我最好使用Collection。有一个集合 满足您的所有需求。

    【讨论】:

    • 第一个解决方案与顺序解决方案一样好。这比第二个好多少?
    • 我认为其他线程必须等待的时间可以忽略。
    • 有什么理由忽略其他线程被阻塞的持续时间?
    【解决方案4】:

    在 java 中,根据您打算如何使用数组,可以将值放入 AbstractQueue ArrayList 中。这是一个有用的java.util.concurrent.ConcurrentLinkedQueue

    或任何类似的队列,例如

    • ArrayBlockingQueue
    • 延迟队列
    • LinkedBlockingDeque
    • 同步队列

    【讨论】:

    【解决方案5】:

    这取决于线程是只写值还是读值(例如,将值加一)。如果他们读取,则在覆盖所有值的区域上同步 - 读取和写入。如果从不同的单元格读取,则第一个变体。如果仅来自同一销售,则为第二个变体。如果没有读取任何值,则根本不需要同步。

    【讨论】:

      猜你喜欢
      • 2017-11-23
      • 1970-01-01
      • 1970-01-01
      • 2016-11-10
      • 2021-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多