У меня есть многопоточное приложение Java 1.6 (5-7 потоков, большинство из них простаивает), которое ведет себя странно.
Поток включает обновление устройства с 4-байтовым идентификатором.
Я храню идентификатор в частном массиве байтов. При успешном обновлении через ~ 4 секунды устройство отправляет сообщение STATUS, в котором я сравниваю его идентификатор с тем, который у меня есть, очищаю частный массив байтов и отключаю таймер ошибки.
Вся работа выполняется в экземпляре класса singleton.
Странное поведение:
Я печатаю значение частного байтового массива из метода, который вызывается периодически.
В течение 4 секунд ожидания сообщения STATUS в журнале отображается другой идентификатор (не мусор, а 4-байтовый идентификатор другого объекта). Проверка значения с помощью точки останова показывает это недопустимое значение (то есть это не ошибка журнала) .
Но, тем не менее, когда приходит сообщение STATUS, я сравниваю идентификатор с тем, который у меня есть, и они совпадают!
Я переместил частный член в синхронизированный метод получения / установки, добавив журнал изменений, который не обнаруживает проблемы.
Вот псевдокод моего установщика / получателя и периодического статуса + тревожный журнал:
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 никогда не копируется. Я попробую это и обновлю.
Progman - даже с помощью фрагментов кода вы заметили проблему. Я не являюсь родным Java-программистом и меня бросили на этот большой кусок старого кода. Вы заслуживаете похвалы.




Как указал Прогман, readerID передается по ссылке. Он поступает с транспортного уровня, удерживается там как член-экземпляр и обновляется следующим входящим сообщением с новым отображаемым идентификатором.
Вам нужно будет показать соответствующий код.