Почему синхронизация дорогая в Java?

Я действительно новичок в Java, и я прочитал это synchronized "очень дорого" в Java. Все, что я хочу знать, это то, что дорого и как это дорого?

Благодарю.

5 ответов

Решение

Может быть, это не так плохо, как вы думаете

Раньше это было ужасно (возможно, поэтому вы читали, что это было "очень дорого"). Эти мемы могут долго вымирать

Вот хорошая статья об этом

Сколько стоит синхронизация?

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

К счастью, постоянные улучшения в JVM позволили как улучшить общую производительность Java-программ, так и снизить относительную стоимость синхронизации с каждым выпуском, и ожидаются будущие улучшения. Кроме того, затраты на синхронизацию часто завышены. Один известный источник цитирует, что вызов синхронизированного метода в 50 раз медленнее, чем вызов несинхронизированного метода. Хотя это утверждение может быть правдой, оно также вводит в заблуждение и заставляет многих разработчиков избегать синхронизации даже в тех случаях, когда это необходимо.

Сказав это, параллельное программирование все еще может быть медленным, но не так много, это теперь просто ошибка Java. Существует компромисс между тонкой и грубой блокировкой. Слишком грубый, очевидно, плохо, но возможно и слишком хорошо, так как замки имеют ненулевую стоимость.

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

Это дорого, потому что, если вы используете потоки, и нескольким потокам приходится проходить через синхронизированный раздел кода, одновременно может выполняться только один из них.

Это как узкое место.

Это даже дорого, когда вы используете один поток, потому что он должен все равно проверить, разрешено ли ему работать.

Если вы сократите использование синхронизированных сегментов, вашему потоку не придется останавливаться, чтобы посмотреть, могут ли они работать (конечно, им не нужно обмениваться данными)

Общий обзор того, как работает синхронизация, можно найти здесь.

http://img20.imageshack.us/img20/2066/monitor28synchronizatioc.png

Монитор в стиле Java

Это не так специфично для Java. Синхронизация может считаться "дорогой" в любой многопоточной среде, если она не выполнена правильно. Является ли это особенно плохо в Java, я не знаю.

Это предотвращает одновременное выполнение потоков, если они используют один и тот же ресурс. Но, поскольку они используют один и тот же ресурс, лучшего варианта нет (это нужно сделать).

Проблема в том, что люди часто защищают ресурс слишком большой областью действия. Например, плохо спроектированная программа может синхронизировать весь массив объектов, а не каждый отдельный элемент в массиве (или даже часть массива).

Это будет означать, что поток, пытающийся прочитать элемент 7, должен ждать чтения или записи потока элементом 22. Не обязательно. Если бы детализация синхронизации была на уровне элементов, а не на уровне массива, эти два потока не мешали бы друг другу.

Только когда два потока попытались получить доступ кодному и тому же элементу, возникнет конфликт ресурсов. Вот почему общее правило состоит в том, чтобы защищать только настолько малый ресурс, насколько это возможно (конечно, с учетом ограничений по количеству синхронизаций).

Но, честно говоря, не имеет значения, насколько дорого обходится, если альтернативой является повреждение данных из-за двух потоков, борющихся за один ресурс. Правильно пишите свое приложение и беспокоитесь о проблемах с производительностью только в том случае, если и когда они появятся ("Начните сначала работать, а затем работайте быстрее" - моя любимая мантра)

Эта статья в IBM на самом деле очень хорошо суммирует основные моменты синхронизации.

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

Другие ответы дают хороший уровень технических деталей, которые я не собираюсь повторять.

Что я сделаю, так это посоветую вам проверить даты статей (а также подразумеваемую компетентность и осведомленность автора). Синхронизация в Java была очень медленной в более ранних версиях JVM. Однако в последнее время он значительно улучшился, так что неконтролируемая синхронизация намного быстрее, чем вы думаете, а также улучшилась неконтролируемая синхронизация.

Имейте в виду, этот вопрос, возможно, не имеет значения - если вам нужно синхронизировать, чтобы гарантировать правильность, вам нужно синхронизировать, чтобы гарантировать правильность. Единственный раз, когда я вижу скорость, это проблема, если вы рассматриваете возможность создания реализации без блокировки (используя очень эффективный, но сложный http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/AbstractQueuedSynchronizer.html) или, возможно, рассматриваете возможность использования другого языка вместо своей задачи.

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

Другие вопросы по тегам