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

Java-同步基元数组中的单个元素,java,multithreading,Java,Multithreading,所以我有一个包含很多整数的数组,我在任何地方都向它写入一个位来存储信息。像这样: public class myclass { int[] bitArray; //methods public myclass() { bitArray = new int[200000000]; //200 million ints } public void flagBit(int arrayIndex, bitIndex) {

所以我有一个包含很多整数的数组,我在任何地方都向它写入一个位来存储信息。像这样:

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);
    }
}
所以,我想知道最有效或最好的方法是什么?我知道原子整数阵,但我相信我能比它更有效率。我已经尝试提出一个解决方案,这就是我得到的(我还没有测试它,它有效吗?)


那么,有没有比我发布的方式更有效的方法呢?我张贴的方式有用吗?我的方法会很有效吗?谢谢~

您可以在不进行任何同步的情况下设置位int数组的读/写是原子的,
除非您执行读修改写操作。

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


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

引人入胜的是,他们用Java 8更新了
AtomicIntegerArray
类,以实际添加允许您使用它来实现此功能的功能-不,你真的没有比这更有效的了

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

至少还是教育性的。这是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));
}

原子集成阵列将比您提出的更有效。事实上,我相信它会表现得很好。你测试过了吗?去试试吧。你会在这里遇到一个数据库设计挑战。是否锁定表、页或行。祝你好运。大小为200M的阵列效率不高。你不能修饰生物废物。@Ricky考虑到使用该类实际实现这些东西的唯一方法是在1.8中引入的,链接1.7文档不会有多大好处;)现在,您只需了解如何在不执行读-修改-写操作的情况下设置位。。。
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));
}