Java 同步阵列中的元素
我不熟悉Java中的多线程,不太了解正在发生的事情 从在线教程和课堂讲稿中,我知道必须应用于非空对象的Java 同步阵列中的元素,java,arrays,multithreading,wrapper,thread-synchronization,Java,Arrays,Multithreading,Wrapper,Thread Synchronization,我不熟悉Java中的多线程,不太了解正在发生的事情 从在线教程和课堂讲稿中,我知道必须应用于非空对象的synchronized块确保只有一个线程可以执行该代码块。因为数组在Java中是一个对象,所以可以对其应用同步。此外,如果数组存储对象,我也应该能够同步数组的每个元素 我的程序有几个线程更新了一个数字数组,因此我创建了一个Long对象数组: synchronized (grid[arrayIndex]){ grid[arrayIndex] += a.getNumber(); } 这段
synchronized
块确保只有一个线程可以执行该代码块。因为数组在Java中是一个对象,所以可以对其应用同步。此外,如果数组存储对象,我也应该能够同步数组的每个元素
我的程序有几个线程更新了一个数字数组,因此我创建了一个Long
对象数组:
synchronized (grid[arrayIndex]){
grid[arrayIndex] += a.getNumber();
}
这段代码位于我扩展的thread类的
run()
方法中。数组grid由我的所有线程共享。但是,在一个线程上运行相同的程序时,这不会返回正确的结果。这将不起作用。重要的是要认识到,grid[arrayIndex]+=…
实际上是在用新对象替换grid
中的元素。这意味着您正在阵列中的一个对象上进行同步,然后立即用阵列中的另一个对象替换该对象。这将导致其他线程锁定在不同的对象上,以便它们不会阻塞。必须锁定一个常量对象
如果从未被另一个数组对象替换,则可以锁定整个数组对象:
synchronized (grid) {
// this changes the object to another Long so can't be used to lock
grid[arrayIndex] += a.getNumber();
}
这就是为什么锁定final
对象是一个好模式的原因之一。有关更多详细信息,请参见此答案:
另一种选择是使用
AtomicLong
对象数组,并使用它们的addAndGet()
或getandad()
方法。你不需要同步来增加你的对象,多个对象可以同时增加。你这里的synchronized
块不好。当您在数组元素上同步时(可能是一个数字),您只在该对象上同步。当您将数组的元素重新分配给一个与开始时不同的对象时,同步不再在正确的对象上,其他线程将能够访问该索引
这两个选项中的一个更为正确:
private final int[] grid = new int[10];
synchronized (grid) {
grid[arrayIndex] += a.getNumber();
}
如果网格
不能是最终的
:
private final Object MUTEX = new Object();
synchronized (MUTEX) {
grid[arrayIndex] += a.getNumber();
}
如果使用第二个选项且网格
不是最终
,则对网格
的任何分配也应同步
synchronized (MUTEX) {
grid = new int[20];
}
始终对最终的内容进行同步,始终对访问和修改进行同步,一旦关闭,您就可以开始研究其他锁定机制,例如
锁定
,读写锁定
,以及信号灯
。这些可以提供比同步更复杂的锁定机制,这对于仅Java的默认同步还不够的情况更好,例如锁定高通量系统中的数据()或锁定资源池()。Java类Long是不可变的,不能更改其值。因此,当您执行操作时:
grid[arrayIndex] += a.getNumber();
它并没有改变您正在锁定的grid[arrayIndex]的值,而是实际创建了一个新的Long对象,并将其值设置为旧值加上.getNumber。因此,不同的线程将在不同的对象上进行同步,这将导致您看到的结果您是对的,但锁定整个阵列可能有点过火。可以使用lock对象设置同级数组final Object[]locks=new Object[arrayLength];对于(int i=0;i
final
不足以保证其内部元素不会更改。OP仅保护数组赋值@ashirley。在这种情况下,锁的粒度并不重要。@Brian是的,总比没有好though@Gray没错,但值得指出的是,如果它演变成一个更为冗长的操作,那就好了@JB。当然你还是要为记忆障碍付出代价。我不同意最后一段。这些锁需要更仔细的编程(try/finally),因此不能称之为“更安全”或“更可预测”,也不能“避免很多问题”。事实恰恰相反。功能更强大,在某些情况下性能更好,是的。同样,在第一种情况下,您不在数字上同步,而是在对象上同步。您还使用了具有误导性的单词value。@格雷我将在单词safe上让步并删除它。只要您遵循正确的同步模式,它就会安全。至于可预测性,本机Java同步是不可预测的。如果有两个或多个线程在等待监视器,则接收监视器的线程是随机的。对于其他系统,我可以打开公平性,让它对线程进行排队,并提供锁先到先服务,而不是随机顺序,从而防止线程饥饿等情况。根据你的第二个评论,我同意,措辞已经改变。有趣的是:可预测性,但我怀疑这是一个学术问题,与语言定义@Brian不同。你知道有哪种情况在实践中是这样的吗?因此,我认为使用Lock
而不是synchronized
是非常危险的。特别是当你为新手线程程序员提供咨询时。@Gray据我所知,调度程序唤醒的第一个在监视器上阻塞的线程就是得到它的线程,所以在实践中,如果处理器调度程序的算法以总是将一个线程置于其他线程之后的顺序唤醒线程,则这是可能的。我想在更实际的意义上,大多数系统(Windows和*NIX)都有很好的调度算法,可以