предисловие
До JDK 1.6 производительность synchronized вызывала беспокойство, но после версии 1.6 команда JVM сделала много оптимизаций для synchronized, так что с точки зрения производительности synchronized сравнима с ReentrantLock. Итак, какие оптимизации сделала команда JVM?
Прежде всего, как мы можем оптимизировать? Мы знаем, что "блокировка" на самом деле является конкретной реализацией синхронизации взаимного исключения, и наибольшее влияние синхронизации взаимного исключения на производительность оказывает реализация блокировки.Операции приостановки потоков и восстановления потоков необходимо перевести из пользовательского режима в режим ядра. завершить. Эти операции оказывают большое давление на одновременную производительность системы.
Поэтому направление оптимизации — уменьшить блокировку потоков, ведь приостановка и возобновление потоков требует переключения в состояние ядра операционной системы.
Чтобы уменьшить потерю производительности, вызванную получением и освобождением блокировок, в Java 1.6 были введены «смещенные блокировки» и «облегченные блокировки». В Java SE 1.6 существует 4 состояния блокировки, от низкого до высокого: нет Состояние блокировки, смещенное состояние блокировки, облегченное состояние блокировки, тяжелое состояние блокировки. Эти состояния будут постепенно обостряться (т. е. раздуваться) в условиях гонки. Примечание. После обновления замка его нельзя будет понизить (конкретные причины будут объяснены позже).
- Блокировка смещения
- Легкий замок
- тяжелый замок
- снятие блокировки
- блокировка огрубления
- Кроме виртуалки как сами программисты оптимизируют блокировки
1. Блокировка смещения
Основываясь на опыте, команда виртуальной машины обнаружила, что в большинстве случаев не только нет многопоточной конкуренции за блокировки, но и один и тот же поток всегда запрашивается несколько раз. вводятся замки.
Когда поток получает доступ к синхронизированному блоку и получает блокировку, он сохраняет идентификатор потока, связанного с блокировкой, в записи блокировки в заголовке объекта и кадре стека.В будущем потоку не нужно выполнять операции CAS для блокировки и разблокировки. при входе и выходе из синхронизированного блока просто проверьте, хранит ли «Mark Word» заголовка объекта предвзятую блокировку, указывающую на текущий поток.
Если проверка прошла успешно, поток получил блокировку. Если проверка не пройдена, необходимо еще раз проверить, установлен ли флаг предвзятой блокировки в Mark Word в 1 (указывающий текущую или предвзятую блокировку): если он не установлен, используйте CAS для борьбы за блокировку; если он установлен, попробуйте использовать CAS для установки заголовка объекта. Смещенные блокировки указывают на текущий поток.
Можно сказать, что «смещение» смещенной блокировки — это смещенное «смещение», что означает, что блокировка будет смещена в сторону первого потока, который ее получит.Если блокировка не используется другими потоками в последующем процессе выполнения получить, поток, удерживающий смещенный замок, будетникогда не нужно синхронизировать.
Когда есть другой поток, пытающийся получить блокировку, объявляется смещенный режим, и последующие операции будут обновлены до упрощенных блокировок.
Примечание: предвзятые блокировки могут повысить производительность программ с синхронизацией, но без состязаний.Они также имеют недостатки: если к большинству блокировок в программе всегда обращаются несколько разных потоков, то предвзятый режим является избыточным. Виртуальные машины после версии 1.6 по умолчанию включают смещенную блокировку, которую можно отключить с помощью параметра JVM: -XX:-UseBiasedLocking=false; по умолчанию программа войдет в состояние облегченной блокировки.
Как видите, Mark Word — это ключ к реализации предвзятой блокировки. Последняя облегченная блокировка также реализована через это.
2. Легкий замок
Что такое облегченный замок? «Легкие» блокировки относятся к традиционным блокировкам, реализованным с использованием мьютексов операционной системы, поэтому традиционные механизмы блокировки называются «тяжеловесными» блокировками. Прежде всего, следует подчеркнуть, что облегченные блокировки не используются для замены тяжеловесных блокировок, его первоначальное намерение состоит в том, чтобы уменьшить потерю производительности, вызванную традиционными тяжеловесными блокировками, с использованием мьютексов операционной системы без многопоточной конкуренции.
Перед выполнением потока синхронизированный блок JVM сначала будет создать пространство для хранения записи блокировки в рамках стека текущей резьбы и скопируйте слово «Отметить слово» заголовка объекта в запись блокировки, которая официально называется смещенной меткой Word. , Затем нить пытается использовать CAS, заменяет слово MARK в заголовке объекта с указателем на запись блокировки.
В случае успеха текущий поток для приобретения блокировки, и если это не удается, представляет другое различие блокировки потока, текущий поток постарается использовать для получения спина Lock Note: эта нить не вешает себя, а на определенном количестве вращений (по умолчанию 10 раз, будьте использованы -xx: модификация Preblockspin), чтобы предотвратить переключение в режим режима ядра в накладных расходах.
Если за одну и ту же блокировку борются более двух потоков, облегченная блокировка будет аннулирована и заменена на тяжеловесную.
Так почему же вы не можете понизить рейтинг после обновления до тяжелого замка? Предположим: если блокировка обновлена до тяжеловесной, поток, который получает блокировку, заблокирован и ожидает в течение длительного времени, тогда легковесная нить будет продолжать вращаться и ждать, потребляя производительность ЦП. Таким образом, после обновления до тяжеловесной блокировки ее нельзя понизить, чтобы предотвратить использование ЦП вращением облегченной блокировки.
Вы можете увидеть разницу между предвзятой блокировкой и облегченной блокировкой.После того, как первый поток получит блокировку, предвзятая блокировка сохранит идентификатор потока в заголовке объекта.Все последующие операции не синхронизируются, что эквивалентно отсутствию блокировки. Для облегченных блокировок вам все равно нужно использовать CAS для изменения записи заголовка объекта каждый раз, когда вы получаете блокировку.При отсутствии конкуренции потоков эта операция очень легковесна и не требует использования механизма взаимного исключения операционной системы. .
3. Тяжелый замок
По сравнению с облегченными блокировками, блокировки приобретаются путем вращения, а тяжелые блокировки реализуются операционной системой, которая переключает потоки в состояние ядра и блокирует. Цена очень высока.
Рассмотрим преимущества и недостатки каждого замка:
Замок | преимущество | недостаток | Применимая сцена |
---|---|---|---|
Блокировка смещения | Блокировка и разблокировка не требуют дополнительного потребления, а разрыв составляет всего наносекунду по сравнению с выполнением асинхронных методов. | Если между потоками возникает конкуренция за блокировку, это приведет к дополнительному потреблению отзыва блокировки. | Подходит для сценариев, когда только один поток обращается к синхронизированным блокам. |
Легкий замок | Конкурирующие потоки не будут блокироваться, что повышает скорость отклика программы. | Использование spin потребляет процессор, если вы никогда не получаете поток, борющийся за блокировку. | В погоне за временем отклика синхронизированные блоки выполняются очень быстро. |
тяжелый замок | Конкуренция потоков не использует вращение и не потребляет ЦП | Блокировка потока и медленное время отклика | Следите за пропускной способностью, блок синхронизации выполняется дольше |
Когда использовать какой замок, мы можем видеть.
4. Снятие блокировки
Что такое снятие блокировки? Это означает, что компилятор JIT устраняет синхронизированные блокировки некоторого кода, который не нужно синхронизировать во время выполнения. Можно сказать, что тщательная оптимизация блокировки. Благодаря устранению блокировок вы можете сэкономить время на бессмысленные запросы блокировок.
Тогда вы должны спросить, кто будет так глупо синхронизировать без синхронизации?
См. код ниже:
public String[] createStrings(String[] args) {
Vector<String> v = new Vector<>();
for (int i = 0; i < 100; i++) {
v.add(Integer.toString(i));
}
return v.toArray(new String[]{});
}
ПРИМЕЧАНИЕ. Переменная V используется только в этом методе, только просто простая локальная переменная, выделенная в стеке, нет причин для того, чтобы безопасность не существует ненужным, а векторная операция добавления синхронизации. Следовательно, виртуальная машина обнаруживает, что эта ситуация будет удалена.
Устранение блокировок включает в себя один метод: анализ выхода. Так называемый анализ побега заключается в наблюдении за тем, что определенная переменная, скорее всего, выйдет за пределы определенной области. В этом примере переменная v не экранирует функцию, и если функция возвращает не массив строк, а саму v, то задача v экранирует текущую функцию. То есть к v могут обращаться другие потоки. Если это так, виртуальная машина не может исключить операцию блокировки v.
5. Огрубление блокировки
В принципе, при написании кода всегда рекомендуется, чтобы синхронизируемые блоки были как можно меньше. Это сделано для того, чтобы количество операций, которые необходимо синхронизировать, было небольшим.Если существует конкуренция за блокировку, потоки, ожидающие блокировки, могут получить блокировку как можно скорее.
В большинстве случаев этот принцип верен. Если серия последовательных операций многократно блокирует и разблокирует один и тот же объект, даже если операция блокировки происходит в теле цикла, даже если нет конкуренции потоков, частые операции синхронизации приведут к ненужным потерям производительности.
Если виртуальная машина обнаружит, что один и тот же объект блокируется множеством разрозненных операций, она расширит (приблизит) область синхронизации блокировки за пределы всей последовательности операций. То есть блок синхронизации увеличивается.
6. Помимо виртуальной машины, как программисты могут сами оптимизировать блокировки
-
Уменьшить время удержания блокировки.
-
Уменьшите детализацию блокировки.
-
Замена эксклюзивных блокировок блокировками чтения-записи
-
разделение замка
-
Уменьшить время удержания блокировки.
На самом деле это очень просто.Долгое время вы держите блокировку,долгое время ожидания следующих потоков.Один поток ждет 1 секунду,а 10000 потоков ждут еще 10000 секунд.Поэтому синхронизируйтесь только при необходимости,чтобы Может значительно сократить время, в течение которого поток удерживает блокировку. Улучшить пропускную способность системы.
- Уменьшите детализацию блокировки.
И мы сказали выше, эта виртуальная машина, чтобы помочь нашему борьбе с шероховатой. Но мы говорим, что в большинстве случаев снижающая гранулярность блокировки также является эффективным средством для ослабления конкурентоспособных многопоточных, таких как ConcurrentHASHMAP, он только заблокировал одно ведроское хешащее ведро, не хмарируемое как для блокировки всего объекта.
- Замена эксклюзивных блокировок блокировками чтения-записи
Когда мы говорили о трех блокировках в мире Java ранее, о трех блокировках, встроенных блокировках, блокировках повторного входа и блокировках чтения-записи, речь идет о блокировках чтения-записи, о которых мы говорим сейчас, ReadWriteLock, использовании чтения-записи. Блокировки записи для замены монопольных блокировок — это уменьшение степени детализации блокировки. В частном случае блокировки чтения-записи выгодны для производительности системы в случае большего количества операций чтения и меньшего количества операций записи. Это может эффективно улучшить возможности параллелизма системы. Поскольку операция чтения не повлияет на целостность и непротиворечивость данных, как и метод get ConcurrentHashMap, блокировка вообще не нужна, а пока поговорим о HashTable, который блокирует даже метод get контейнера. . Ты можешь представить.
- разделение замка
Если блокировка чтения-записи дополнительно расширена, это разделение блокировки Блокировка чтения-записи эффективно разделена в соответствии с различными функциями операции чтения-записи. LinkedBlockingQueue JDK — лучший способ разделения блокировок. Две разные блокировки используются для операции взятия и операции размещения. Потому что между ними вообще нет конкуренции, иными словами, использование структуры данных очереди разделяет изначально связанный бизнес.
7. Резюме
Сегодня мы суммируем некоторые оптимизации блокировок, в том числе оптимизации виртуальных машин, такие как предвзятые блокировки, облегченные блокировки, спин-блокировки, огрубление блокировок, устранение блокировок, а также наши собственные стратегии оптимизации, на которые необходимо обращать внимание при написании кода. время удерживания блокировок, уменьшить степень детализации блокировок, использовать блокировки чтения-записи в тех случаях, когда больше операций чтения и меньше операций записи, и попытаться разделить блокировки посредством разумного проектирования.
Короче говоря, параллелизм — это искусство. Как улучшить параллельную производительность — это стремление каждого старшего программиста.
удачи! ! !