Java - 同步基本数组中的各个元素

时间:2014-07-31 23:05:46

标签: java multithreading

所以我有一个包含很多整数的数组,我正在为它编写单个位来存储信息。像这样:

public class myclass
{
    int[] bitArray;

    //methods

    public myclass()
    {
        bitArray = new int[200000000]; //200 million ints
    }

    public void flagBit(int arrayIndex, bitIndex)
    {
        /** 10000000 00000000 00000000 00000000 >>> bitIndex */    
        bitArray[arrayIndex] |= (0x80000000 >>> bitIndex);
    }
}

现在,正如您所看到的,这是很多数据。因此,效率势在必行。

无论如何,我希望多个线程(安全地)立即将数据写入此数组。不幸的是,在Java中,您无法执行以下操作:

public void flagBit(int arrayIndex, bitIndex)
{
    /** 10000000 00000000 00000000 00000000 >>> bitIndex */

    synchronized (bitArray[arrayIndex]) //wrong!!! must synchronize to object!
    {
        bitArray[arrayIndex] |= (0x80000000 >>> bitIndex);
    }
}

所以,我想知道最有效或最好的方法是什么?我知道AtomicIntegerArray,但我相信我可以比这更有效率。我试图提出一个解决方案,这就是我得到的(我还没有测试过它,它有效吗?)

public class myclass
{
    int[] bitArray;
    SYNC[] indexSync;

    //inner class(es)

    private static final class SYNC
    {
        private static final boolean sync = false;
    }

    //methods

    public myclass()
    {
        bitArray = new int[200000000]; //200 million ints

        indexSync = new SYNC[bitArray.length];
    }

    public void flagBit(int arrayIndex, bitIndex)
    {
        /** 10000000 00000000 00000000 00000000 >>> bitIndex */    

        synchronized (indexSync[arrayIndex])
        {
            bitArray[arrayIndex] |= (0x80000000 >>> bitIndex);
        }
    }
}

那么,有没有比我发布的方式更有效的方法呢?我的发布方式是否有效?我的方式会非常有效吗?感谢〜

3 个答案:

答案 0 :(得分:1)

您应该只使用AtomicIntegerArray。它似乎完全符合您的需求,并且是由专家编写的经过良好测试的代码。你应该在扣除它之前测试它的性能。

如果你真的不想使用AtomicIntegerArray,你真的不需要SYNC类,你可以只使用布尔对象。另外,如果你要创建一个完整的第二个SYNC对象数组,那么制作你的第一个数组整数而不是整数可能是值得的。

答案 1 :(得分:1)

引人入胜,他们用Java 8更新了AtomicIntegerArray类,实际上添加了允许你使用它实现这一功能的功能 - 不,你真的不能比它更有效。

事实上,它比你正在做的更有效率,因为它避免了锁定整体。对于那些坚持使用Java 7或之前的人 - 或者只是想了解其工作原理的人,这里是我在检查新Javadoc之前实际编写的简化版本。

至少教育仍然。这是Java中一个非常基本的比较和交换循环。在许多情况下非常有用,虽然看起来更新AtomicIntegerArray(也可能是其他类),但对于那些有兴趣从代码中获得最佳性能的人来说,这可能不再那么必要或有用。一旦你将样板代码排除在外,它仍然非常简单。

private static final Unsafe unsafe = getUnsafe();
private static final long arrayBase = getArrayBase();
private static final long arrayScale = getArrayScale();

private static Unsafe getUnsafe() {
    try {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        return (Unsafe) theUnsafe.get(null);
    } catch (NoSuchFieldException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

private static long getArrayBase() {
    return unsafe.arrayBaseOffset(int[].class);
}

private static long getArrayScale() {
    return unsafe.arrayIndexScale(int[].class);
}

private final int[] array = new int[1000];

public void flagBit(int arrayIndex, int bitIndex) {
    // offset of array[arrayIndex] from start of array. In practice you'd want to compute the log2 
    // of arrayScale and just do a shift.
    long offset = arrayBase + arrayIndex * arrayScale;
    int oldValue, newValue;
    do {
        // Read array[arrayIndex] volatile, we need the memory ordering guarantee
        oldValue = unsafe.getIntVolatile(array, offset);
        // Compute new value we want
        newValue = oldValue | (0x80000000 >>> bitIndex);
        // If the field wasn't updated, update it with newValue and return true, otherwise
        // return false and we retry.
    } while (!unsafe.compareAndSwapInt(array, offset, oldValue, newValue));
}

答案 2 :(得分:0)

你可以在没有任何同步读/写int数组的情况下设置位是原子的, 除非你做了读 - 修改 - 写操作。