1

我有一个长度为 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
      } 
    }
  }

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

4

5 回答 5

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;   
}

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

于 2013-08-02T07:34:59.460 回答
0

我会选择解决方案1:

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

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

synchronize 

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

提示:我不会使用数组。我最好使用Collection。有一个适合您需要的每个人的集合。

于 2013-08-01T10:58:25.870 回答
0

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

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

于 2013-08-01T10:50:52.173 回答
0

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

于 2013-08-01T12:45:34.997 回答
0

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

或任何类似的队列,例如

  • 数组阻塞队列
  • 延迟队列
  • LinkedBlockingDeque
  • 同步队列
于 2013-08-01T10:54:58.503 回答