同步数组中的元素

时间:2012-09-06 16:38:57

标签: java arrays multithreading wrapper thread-synchronization

我不熟悉Java中的多线程,也不太了解发生了什么。

从在线教程和讲义中,我知道必须应用于非null对象的synchronized块确保只有一个线程可以执行该代码块。由于数组是Java中的对象,因此可以对其应用同步。此外,如果数组存储对象,我应该能够同步数组的每个元素。

我的程序有几个线程更新了一个数组,因此我创建了一个Long个对象数组:

synchronized (grid[arrayIndex]){
    grid[arrayIndex] += a.getNumber();
}

这段代码位于我扩展的线程类的run()方法内。数组网格由我的所有线程共享。但是,在一个线程上运行相同的程序时,这不会返回正确的结果。

4 个答案:

答案 0 :(得分:7)

这不起作用。重要的是要意识到grid[arrayIndex] += ...实际上是用新对象替换grid中的元素。这意味着您正在同步数组中的对象,然后立即将该对象替换为数组中的另一个对象。这将导致其他线程锁定不同的对象,因此它们不会阻塞。 必须锁定常量对象。

您可以改为锁定整个数组对象,如果它永远不会被另一个数组对象替换:

synchronized (grid) {
    // this changes the object to another Long so can't be used to lock
    grid[arrayIndex] += a.getNumber();
}

这是锁定final对象的好模式的原因之一。请参阅此答案以了解更多详情:

  

Why is it not a good practice to synchronize on Boolean?

答案 1 :(得分:2)

另一种选择是使用AtomicLong个对象数组,并使用他们的addAndGet()getAndAdd()方法。您不需要同步来增加对象,并且可以同时增加多个对象。

答案 2 :(得分:1)

java类Long是不可变的,你不能改变它的值。所以当你执行一个动作时:

grid[arrayIndex] += a.getNumber();

它不会更改您锁定的grid [arrayIndex]的值,但实际上是创建一个新的Long对象并将其值设置为旧值加上a.getNumber。因此,您将最终在不同的对象上同步不同的线程,这将导致您看到的结果

答案 3 :(得分:0)

你在这里的synchronized区块并不好。当您对数组元素(可能是数字)进行同步时,您只在该对象上进行同步。当您将数组的元素重新分配给与您开始的对象不同的对象时,同步不再在正确的对象上,而其他线程将能够访问该索引。

这两个选项中的一个更正确:

private final int[] grid = new int[10];

synchronized (grid) {
    grid[arrayIndex] += a.getNumber();
}

如果grid不能final

private final Object MUTEX = new Object();

synchronized (MUTEX) {
    grid[arrayIndex] += a.getNumber();
}

如果您使用第二个选项且grid不是final,则还应同步grid的任何分配。

synchronized (MUTEX) {
    grid = new int[20];
}

始终在最终内容上进行同步,始终在访问和修改上同步,一旦完成,您就可以开始研究其他锁定机制,例如LockReadWriteLock和{{ 1}}。这些可以提供比同步更复杂的锁定机制,这对于单独的Java默认同步不够的情况更好,例如在高吞吐量系统(read/write locking)中锁定数据或锁定资源池({{3 }})。