Параллельность заключается в том, что вам нужно снизить свои ожидания в отношении определенности и предсказуемости. Компьютер делал это, когда вы работали однопоточно. Теперь, когда вы имеете дело с параллелизмом, это не так. Вы не можете спросить «почему это так или иначе» или «почему это так или иначе»? Все, что вы можете спросить, является ли что-то гарантировано или нет.
Две версии кода дают одинаковые результаты, когда вы пробуете его на своем компьютере, используя ваши версии ваших инструментов, потому что ничто не требует, чтобы они вели себя по-разному. Ничто не требует и того, что они будут делать то же самое. Что-то произойдет с этими кодами, и вы можете предсказать некоторые из них, а не остальные.
В любом случае, разница с синхронизированным методом или переменной volatile заключается в том, что любой доступ, чтение или запись к переменной volatile гарантированно будет выполняться так, как если бы существовала только одна основная память, а кэш локальной памяти потока не существовал. В то время как синхронизированные методы делают это только при вводе синхронизированного метода. Здесь вы выполняете синхронизацию только при чтении переменной и не выполняете надлежащей обработки при записи.
Это означает, что у вашего примера с синхронизацией было много шансов не дать ожидаемых результатов. Не то чтобы это гарантированно провалилось. Может, так и будет, а может и нет. Все, что мы можем сказать, это то, что гарантировано правильным программированием. Мы не можем сказать, что произойдет с тем, что ваши программы оставили неизвестными.