Являются ли синхронные методы медленнее в однопоточных приложениях?

Я обсуждал это сам в течение последних нескольких минут, и я вижу причины как для да, так и для нет. Это связано с поиском ответов на Java HashMap против Hashtable и, увидев, что некоторые люди говорят, что Hashtable на самом деле медленнее.

Мне кажется, что синхронизированный метод должен действовать абсолютно не иначе, как его несинхронизированный аналог, если он работает в одном потоке, так как действие синхронизации не должно блокировать ничего. Тем не менее, я бы предположил, что компилятор обрабатывает два случая по-разному и что люди говорят, что синхронизация медленнее.

Не то, чтобы это каким-либо образом было окончательным, но я провел несколько простых тестов на HashMap vs Hashtable и увидел небольшую разницу в скорости.

Ответ 1

Да, однозадачные Java-программы, использующие синхронизацию, могут быть немного медленнее, чем без синхронизации. Для ранних выпусков Java синхронизация была дорогостоящей. Тем не менее, для любого современного выпуска неподходящая синхронизация довольно дешевая. Я бы не стал беспокоиться об этом.

Обратите внимание, что Java 6 имеет и Java 7 должен иметь хорошие оптимизации вокруг блокировки:

  • Блокировка укрупнения
  • Блокировка elision
  • Адаптивная блокировка спина
  • Смещенная блокировка

Дополнительные сведения см. в Java SE 6 Performance White Paper. Также обратите внимание, что бесконтактная синхронизация, по-видимому, более дорогая для многоядерных процессоров, чем для одноядерных процессоров, возможно, из-за требований к модели памяти Java, связанных с синхронизацией, которые заставляют локальные кэши CPU делиться с другими процессорами или каким-то другим барьером памяти. Например, прочитайте Действительно ли оптимизация потоков Java 6 работает? - Часть II. (Часть я была не такой проницательной, как часть II.)

Ответ 2

Да. Они будут немного медленнее из-за дополнительных накладных расходов на сохранение блокировок.

Ответ 3

При использовании синхронизированных структур данных замедление не зависит от того, "сколько" блокируется. Акт приобретения или выпуска блокировки медленный, поскольку он обычно включает в себя что-то вроде системного вызова (контекстные переключатели на любой платформе медленны). В среде JIT, такой как типичная JVM, теоретически было бы возможно оптимизировать все вызовы блокировки/разблокировки, когда есть только один поток, но он должен быть корректно недействительным всякий раз, когда запускается другой поток.

Обратите внимание, что такие вещи, как Linux futexes, не должны принимать системные вызовы, если не существует конкуренции, но использование их все еще медленнее, чем no-op.