Ключевое слово synchronized Java

Ключевое слово synchronized JavaВ ранних статьях блога вы видели, как надо использовать объекты Lock и Condition. Прежде чем двинуться дальше, подведем итоги, перечислив ключевые моменты, касающиеся блокировок и условий.

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

Интерфейсы Lock и Condition были добавлены в Java SE 5.0, чтобы предоставить программистам высокую степень контроля блокировок. Однако в большинстве ситуации вам не понадобится такой контроль, и вы можете использовать механизм, построенный на средствах языка Java.

Еще со времен версии 1.0 каждый объект Java обладает внутренней блокировкой. Если метод объявлен с ключевым словом synchronized, то блокировка объекта защищает весь этот метод. То есть для того, чтобы вызвать этот метод, поток должен захватить внутреннюю блокировку объекта.

Другими словами, следующее:

является эквивалентом этому:

Например, вместо использования явной блокировки, мы можем просто объявить метод transfer класса Bank, как synchronized.

Внутренний объект блокировки имеет единственное ассоциированное с ним условие. Метод wait() добавляет поток в набор ожидания, а методы notifyAll() и notify() разблокируют ожидающие потоки.

Другими словами, вызов wait() / notifyAll() — это эквиваленты следующего:

Методы wait(), notifyAll() и notify() являются финальными(final) методами класса Object. Методы Condition должны именоваться await, signalAll и signal, так что они не конфликтуют с этими методами.

Например, вы можете реализовать класс Bank следующим образом:

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

Блокировка управляет потоками, которые пытаются войти в метод synchronized. Условие управляет потоками, вызвавшими wait().

Синхронизированные методы относительно просты. Однако начинающие программисты часто испытают затруднения с условиями.

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

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

Внутренние блокировки и условия имеют некоторые ограничения:

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

Что же следует использовать в вашем коде — объекты Lock и Condition или синхронизированные методы? Вот наши рекомендации:

  • Лучше не использовать ни Lock/Condition, ни ключевое слово synchronized. Во многих ситуациях вы можете применять один из механизмов пакета java.util.concurrent, который сделает всю работу по блокировке за вас.
  • Если ключевое слово synchronized подходит для вашей ситуации, обязательно используйте его. Вы напишете меньше кода, и у вас будет меньше шансов допустить ошибку. В программе которая приведена ниже показан пример с банком, реализованный на основе синхронизированных методов.
  • Используйте Lock/Condition, если вы действительно нуждаетесь в дополнительных возможностях этих конструкций.