同时快速读/写int数组

时间:2014-01-21 01:05:58

标签: java concurrency

我需要一种快速的方法来同时读取/写入int数组。 每个索引都需要写入。只需要阅读操作 完成所有写操作后完成数组。

我开始使用同步版本:

public final class SynchronizedIntArray {
    private final int[] elements = new int[1000000];

    public synchronized void set(int index, int value)
    {
        elements[index] = value;
    }

    public synchronized int[] getAll()
    {
        return elements;
    }
}

表现不佳。 我在网上搜索了一个更好的解决方案,找到了AtomicIntegerArray

public final class ConcurrentIntArray
{
    private final AtomicIntegerArray elements = new AtomicIntegerArray(1000000);

    public void set(int index, int value)
    {
        elements.set(index, value);
    }

    public int[] getAll()
    {
        int[] intArray = new int[elements.length()];
        for (int i = 0; i < intArray.length(); i++) {
            intArray[i] = elements.get(i);
        }
        return intArray;
    }
}

但我想知道为什么没有方法可以从中获取完整的int数组 AtomicIntegerArray一下子。 性能仍然比同步版本好很多,但是 我真的需要以这种方式复制它吗?

对于我的问题,是否有比AtomicIntegerArray更快的替代方案?

3 个答案:

答案 0 :(得分:3)

是的,你确实需要以这种方式复制它。 AtomicIntegerArray的实现意味着没有办法锁定整个数组并且在没有任何其他线程同时进行更改的情况下获取它:您一次只能原子地获取一个元素。

答案 1 :(得分:0)

我不是Java程序员,但作为一名实时程序员,我认为你的两个实现在概念上都是错误的。
不过,我可能误解了Java同步的工作原理。在这种情况下,请忽略这篇文章并接受我的道歉。

您的第一个解决方案

确保编写者之间没有并发性,因为您锁定了对允许修改一个元素的唯一函数的访问权。
您还可以锁定整个数组的访问权限,但仅限于读者之间(由于读者不会修改数据,因此可以安全地同时读取数据)这是无用的。

但是,您不会阻止读者和作者之间的并发访问。一个(单个)读取器可能读取由(单个)写入器修改的元素的值。

此外,您返回对成员数组的引用而不是副本,因此调用者将访问作者随时可能会覆盖的实际数据。
因此,即使在您调用读取访问器之后,在写入期间,阵列也完全不受读取访问的保护。

如果由于某种原因对数组元素的实际访问是原子的(我非常怀疑,但是Java不是我的茶),或者更可能因为潜在的不一致很少且不容易检测到,代码可能会起作用。如果您在不同的环境中运行,甚至稍微更改您的代码,那么您的应用程序可能会变得混乱,从而增加了并发读/写的发生,或者更容易受到这些不一致的影响。

您的第二个解决方案

使用与您的先前代码相同的AtomicArray,但为单个数组值 上的并发读取 添加了保护。
它消除了第一个解决方案的潜在不一致性(也是因为这次你的读取器功能被迫返回实际数组的副本),但是读取整个数组是非常低效的,因为你的AtomicArray对象将获取并释放每个读取值的锁。

一种可能的方法

你必须保护的是阵列整体。

在一般情况下,您可能希望使用读取器/写入器锁。这将允许多个读者同时访问阵列 但是,如果我理解正确,在你的情况下只有一个阅读器,所以最好使用一个简单的锁。

单次写入和全局读取的代码如下:

public final class SynchronizedIntArray {
    private final int[] elements = new int[1000000];
    private final Lock lock = new ReentrantLock();

    public void set(int index, int value)
    {
        lock.lock();
        elements[index] = value;
        lock.unlock();
    }

    public int[] getAll()
    {
        int[] copy;
        lock.lock();
        copy = elements.clone();
        lock.unlock();
        return copy;
    }
}

答案 2 :(得分:-1)

是的,对于您的用例,有一个非常好的实现比AtomicIntegerArray更好。 这里的关键是引用和int赋值是原子的。编译器未优化volatile写入。

//This class is thread safe 
public final class BetterIntArray {

private volatile int[] elements = new int[1000000]; // Don't make it finall

public void set(int index, int value)
{
    elements[index] = value; //assignment is atomic
    elements =  elements; // guarantees that changes are visible to other thread
}

public int[] getAll()
{
    return elements ;
}    

}