Содержание

четверг, 14 апреля 2011 г.

Блокировки в java

Одно из самых полезных вещей на конференциях вроде javaone это стенды, на которых можно пообщаться с инженерами Oracle, непосредственно работающими над JVM. Самый интересный стенд (вернее люди, которые там находились) на только что прошедшей конференции javaone в Москве был "Java Performance". Там мне удалось услышать краткий обзор стратегий блокировки потоков применяемой в HotSpot на данный момент. Дабы не забыть эту весьма интересную и малоосвещенную в литературе информацию, я решил написать данный топик.

Виды блокировок

На данный момент есть три различных видов блокировок: biаsed, thin and fat. Начнем в историческом порядке их появления.
1. Fat блокировка
Самый очевидный и простой вариант, основанный на использовании мютекса операционной системы. Применим в любых условиях, включая случай состязательные блокировок в нескольких потоках.
2. Thin блокировка
Если же состязательной блокировки практически нет, то можно придумать кое-что побыстрее мьютекса, так как будить в этом случае потоки не нужно и можно обойтись без скедульера операционной системы. Так были придуманы thin блокировки использующие CAS операции для гарантирования эксклюзивного доступа.
3. Biased блокировки
На некоторых архитектурах, особенно очень многопроцессорных, CAS операции не очень-то дешевы, поэтому для случая работы с объектом в одного потоке были придуманы biased блокировки, которые не требуют никаких CAS операций. На самом требуется одна CAS операция для выставления типа блокировки при первой попытке её захвата.

Таким образом для состязательных блокировок со многими потоками могут использоваться только fat блокировки, для не состязательных блокировок многими потоками выгоднее использовать thin локи и для работы с одним потоком выгоднее использовать biased блокировки.

Порядок выбора блокировок HotSpot

При работе JVM переход от одного вида блокировки к другому происходит в обратном описанному порядке. Т.е. стратегия на данный момент оптимистическая. При первом захвате блокировки, JVM надеется, что данная блокировка будет использоваться только одним потоком, и использует biased блокировку. Если же данную блокировку пытается захватить другой поток, то biased блокировка конвертируется в fat блокировку в случае состязательного захвата или в thin блокировку в случае не состязательного. Если блокировка была сконвертирована в thin и потом наступил состязательный захват из другого потока, то возможно он немного покрутиться в CAS и если не захватит блокировку через совсем короткий промежуток времени, то блокировка сконвертируется в fat, чтобы можно было пользоваться возможностью скедьюлинга потоков операционной системой.

Применение данного сакрального знания

Казалось бы, какая разница программисту пишущему прикладной код от того, какую блокировку выберет JVM? Например, такой случай. В вашем приложении работа с каким-то объектом выполняется без состязания то одним, то другим потоком. Вы поставили синхронизацию и забыли об этом. Однако если вы знаете о типах блокировок, то вы, возможно, попытаетесь сделать так, чтобы работа с данным объектом практически всегда выполнялась одним и тем же потоком, тем самым JVM сможет задействовать biased блокировку вместо thin. Конечно же я не призываю на этом заморачиваться, но если во время реализации любой из подходов занимает одинаковые усилия, то почему бы сразу не сделать так, чтобы JVM могла использовать более эффективную блокировку.

Второй случай, когда об этом полезно знать, был приведем на одном из докладов javaone. Проводился микро бэнчмарк сравнения производительности synchronized метода и метда с тем же содержимым, все тело которого заключено в synchronozed блок на this.
Сам тест был в виде метода main(), внутри которого сначала вызывался N-ое количество раз метод m1(), затем метод m2(). Результат - различие производительности в несколько раз. Объяснение же этому весьма простое - biased locking оптимизация включается не сразу, а после некоторой задержки, как стартовала JVM. Если тест запускать с параметром -XX:BiasedLockingStartupDelay=0, то время работы методов примерно совпадает.