我不熟悉Java中的多线程,也不太了解发生了什么。
从在线教程和讲义中,我知道必须应用于非null对象的synchronized
块确保只有一个线程可以执行该代码块。由于数组是Java中的对象,因此可以对其应用同步。此外,如果数组存储对象,我应该能够同步数组的每个元素。
我的程序有几个线程更新了一个数组,因此我创建了一个Long
个对象数组:
synchronized (grid[arrayIndex]){
grid[arrayIndex] += a.getNumber();
}
这段代码位于我扩展的线程类的run()
方法内。数组网格由我的所有线程共享。但是,在一个线程上运行相同的程序时,这不会返回正确的结果。
答案 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
对象的好模式的原因之一。请参阅此答案以了解更多详情:
答案 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];
}
始终在最终内容上进行同步,始终在访问和修改上同步,一旦完成,您就可以开始研究其他锁定机制,例如Lock
,ReadWriteLock
和{{ 1}}。这些可以提供比同步更复杂的锁定机制,这对于单独的Java默认同步不够的情况更好,例如在高吞吐量系统(read/write locking)中锁定数据或锁定资源池({{3 }})。