Техника генерирует потокобезопасный код в том смысле, что защищает ваши java-переменные от прерываний чтения / записи из нескольких потоков. Однако это не означает, что ваш код правильный. Иногда синхронизация применяется к последовательностям выполнения, которые плохо представлены одним методом. Представьте себе подпрограмму, которая принимает прямоугольник и устанавливает ширину и длину, равные 5. Представьте себе другую подпрограмму в другом потоке, которая принимает прямоугольник и задает ширину, равную 3, и длину, равную 6. Даже если setWidth и setLength синхронизированы, нить 1 может устанавливать ширину, нить 2 может устанавливать ширину и длину, а нить 1 может устанавливать длину. Теперь прямоугольник имеет ширину 5 и длину 6. Это неверно для любой из нитей. Обратите внимание, что если бы прямоугольник был неизменным, эта конкретная проблема не возникла бы.
Вот пример, который подходит для больших систем. Представьте себе распределенную систему, в которой вам нужно синхронизировать два файла на двух машинах. Вам нужно получить какую-то блокировку синхронизации для каждого файла. Если за разные файлы конкурируют разные потоки, вам необходим механизм, позволяющий установить, кто получает блокировку. Существует множество схем для решения этой проблемы, поэтому это не является неизвестной проблемой, но вы можете видеть, что это не так просто, как две частные переменные в одном объекте.
Теперь, ваше продолжение: каковы недостатки? Если у вас есть неизменный ресурс, вам, вероятно, нет необходимости защищать от многократного чтения разными потоками. Как следствие, дополнительные издержки кода синхронизации не нужны. Ваша программа, хотя и правильная, медленнее, чем другая правильная программа, реализованная с теми же алгоритмами только из-за ненужной синхронизации.