2

我是 Java 多线程的新手,不太了解发生了什么。

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

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

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

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

4

4 回答 4

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这就是为什么锁定对象是一种很好的模式的原因之一。请参阅此答案以获取更多详细信息:

为什么在布尔值上同步不是一个好习惯?

于 2012-09-06T16:40:35.100 回答
2

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

于 2012-09-06T16:43:43.890 回答
1

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

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

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

于 2012-09-06T16:48:53.567 回答
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不是 is final,则任何分配 togrid也应该同步。

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

总是在最终的东西上同步,总是在访问和修改上同步,一旦你完成了,你就可以开始研究其他锁定机制,例如Lock,ReadWriteLockSemaphore. 这些可以提供比同步更复杂的锁定机制,这对于仅 Java 的默认同步还不够的情况更好,例如在高吞吐量系统中锁定数据(读/写锁定)或在资源池中锁定(计数信号量)。

于 2012-09-06T16:47:12.177 回答