【发布时间】:2018-05-22 11:24:22
【问题描述】:
我有一个 Java 1.6 多线程应用程序(5-7 个线程,最空闲),它有一个奇怪的行为。
该流程涉及使用 4 字节 ID 更新设备。
我将 ID 保存在私有字节数组中。更新成功后,大约 4 秒后,设备会发送一条 STATUS 消息,我在其中将其 ID 与我持有的 ID 进行比较,并清除私有 bit-array 并禁用错误计时器。
所有工作都在单例类实例中完成。
奇怪的行为:
我从定期调用的方法中打印私有字节数组的值。
在等待 STATUS 消息的 4 秒内,日志显示不同的 ID(不是垃圾,而是不同对象的 4 字节 ID)。使用断点检查值会显示此无效值(意味着它不是日志错误)。
但是,当 STATUS 消息到达时,我将 ID 与我持有的 ID 进行比较,它们匹配!
我将私有成员移动到同步的 getter/setter 中,添加了更改日志,但没有发现问题。
这是我的 setter/getter 和周期性状态 + 令人不安的日志的伪代码:
public class Manager {
private volatile byte[] readerID = null;
public synchronized void setReaderID(byte[] readerID) {
this.readerID = readerID;
logger.debug("readerID = {}", StringUtilities.binaryToAscii(this.readerID));
}
public synchronized byte[] getReaderID() {
if (this.readerID == null)
return null;
return Arrays.copyOf(this.readerID, this.readerID.length);
}
/* Called every second */
public void periodicStatus() {
logger.debug("readerID = {}", StringUtilities.binaryToAscii(getReaderID()));
}
}
13:53:46,103|ad-5|INFO |Manager|readerUpdateFinish(): Received firmware install finish for reader 000189D0 at slot 0
13:53:46,103|ad-5|DEBUG|Manager|setReaderID(): readerID = 000189D0
13:53:46,103|ad-5|DEBUG|Manager|readerUpdateFinish(): triggered reader firmware timer, 1526986426103, 000189D0
13:53:46,408|ad-5|DEBUG|Manager|periodicStatus(): readerID = E69EAD03 // <- where's the setter???
13:53:50,030|ad-5|INFO |Manager|readerStatus(): Received status information for reader 000189D0 at slot 0
13:53:50,031|ad-5|DEBUG|Manager|setReaderID(): readerID = null
13:53:50,031|ad-5|DEBUG|Manager|readerStatus(): timer cleared, null
有什么想法吗?
【问题讨论】:
-
你将不得不展示一些相关的代码。
-
您必须添加剩余的源代码,这些源代码使用此源代码以供所有人测试。见stackoverflow.com/help/mcve。根据我们没有看到的源代码,您可能会在将引用分配给私有字段后更改数组。
-
添加更多源代码是有问题的;根据配置,它有很多代码,从多个线程运行。我尝试给出最小的,一个记录更改的同步设置器和一个返回副本的获取器,但是日志中的数据似乎发生了变化。更多的是一个哲学 Java 问题(来自 C 语言程序员的眼光)
-
@RamiRosenbaum 您分配给该字段的数组在两个地方是已知的。一个地方是
Manager.readerId字段,另一个地方是你调用setReaderID()方法的地方。请记住,数组是 java 中的对象,因此Manager.readerId字段中的引用与您调用setReaderID()方法的引用相同。因此,当您“在一个地方”更改数组时,您会“在另一个地方”更改数组,因为它实际上是完全相同的数组。这就是我们要求提供完整 MCVE 的原因,因为这样我们就可以告诉您错误在哪里。 -
Progman - 谢谢!实际上,readerID 缓冲区是从通信传输层到达的,并且是实例成员。 readerID 永远不会被复制。我会试试这个并更新。
标签: java corrupt-data