Ошибки синхронизации обычно трудно воспроизвести, потому что они зависят от тонкой синхронизации между различными потоками, поэтому любая реализация, которая на самом деле пытается "просто запустить вашу программу", не всегда может быть "наихудшей из возможных". Вы не можете воспроизвести более одного способа, которым два потока могут чередовать свои инструкции, если вы просто выполните эти инструкции один раз. Тестирование всех таких комбинаций за один проход еще менее возможно. Один из других авторов предложил Java Pathfinder, и это звучит как хорошая идея, но обратите внимание, что это приложение, которое многократно выполняет один и тот же код, так что вы не можете воспринимать это как просто другую реализацию JVM.
Еще один практический совет - попытаться запустить приложение на как можно большем количестве различных виртуальных машин Java. Попробуйте разных поставщиков, разные версии от одного поставщика, разные архитектуры ЦП и так далее. Несколько лет назад у меня был опыт работы с многопоточным приложением, которое было разработано, протестировано и запущено на JVM от Sun на процессорах Xeon, где оно работало очень хорошо. В какой-то момент я попытался запустить его на виртуальной машине Java J9 IBM с архитектурой POWER, и с первой попытки примерно 2/3 тестов не удалось из-за ошибок синхронизации. Таким образом, тестирование в разных средах может быть достаточно хорошим для выявления скрытых проблем синхронизации.