Главная > Java Standard Edition > Блокировки

Тема Зацепин
268

Java-разработчик 🧩
346
1 минуту

Блокировки

Одной из популярных функциональных возможностей библиотек J2SE 5.0 является добавление средств обеспечения параллельной работы. Предоставленные как часть JSR 166 эти средства обеспечивают развитые возможности программирования параллельных процессов, устраняющие необходимость использования разработчиками ключевого слова synchronized и связанных с ним блокировок. Среди предлагаемых ими функциональных возможностей присутствуют: поддержка блокировочных таймаутов, множественные переменные условия для одной блокировки, блокировки чтения/записи и способность прерывать поток, ожидающий снятия блокировки. Более подробную информацию по дополнительной поддержке блокировок можно найти в документации по пакету java.util.concurrent.locks.

Добавлено : 7 Mar 2009, 06:40

Основой пакета является интерфейс Lock, который предлагает набор методов для запроса и снятия блокировки. Типичной последовательностью использования является: получить блокировку, обратиться к защищенному ресурсу, снять блокировку. Вот схема такого использования:

Lock l = ...;
l.lock
();
try {
// доступ к защищенному ресурсу
} finally {
l.unlock();
}

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

synchronized(lockVariable) {
// доступ к защищенному ресурсу
}

Похожа, но не идентична. Если вы не вызовете метод unlock() в блоке finally, ваше приложение не будет работать правильно. В блоке synchronized это не обязательно.

Еще одним методом, предоставляемым интерфейсом Lock и запрашивающим блокировку, является lockInterruptibly(). Поток после запроса блокировки переходит в состояние ожидания. Метод lockInterruptibly() дает вам возможность прервать ожидающий поток, а метод lock() - нет. Если в ожидающем блокировки с lockInterruptibly() потоке вызывается метод interrupt(), ожидание будет прервано. Ожидающий поток просыпается, и генерируется исключительная ситуация InterruptedException. В этом случае попыток доступа к защищенному ресурсу не делается, а продолжаются любые другие действия.

Обычная схема использования метода lockInterruptibly():

Lock l = new ReentrantLock();
try {
l.lockInterruptibly();
try {
// доступ к защищенному ресурсу
} finally {
l.unlock();
}
}
catch (InterruptedException e) {
System.err.println("Interrupted wait");
}

Здесь внутренний блок try-finally обрабатывает исключительные ситуации, возможные при работе с защищенным ресурсом. Внешний блок try-catch обрабатывает исключительные ситуации при запросе ресурса. Вы никогда не должны вызывать unlock, если запрос блокировки терпит неудачу.

Предупреждение: вызов lockInterruptibly() не обязательно означает, что вы сможете прервать ожидание. Не все реализации Lock поддерживают эту операцию.

Еще один метод, предоставляемый интерфейсом Lock, tryLock(), вводит в игру таймауты. Метод tryLock() имеет две версии. Первая версия не принимает аргументов. Она делает попытку получить блокировку и немедленно терпит неудачу при недоступности этой блокировки:

Lock lock = ...;
if (lock.tryLock()) {
try {
// доступ к защищенному ресурсу
} finally {
lock.unlock();
}
}
else {
// альтернативное действие
}

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

Вторая версия метода tryLock принимает два параметра для указания таймаута. Первый аргумент – это переменная long, указывающая максимальное время ожидания. Второй аргумент – это TimeUnit, указывающий единицу времени.

Например, lock.tryLock(300, TimeUnit.MILLISECONDS) делает попытку получить блокировку. Если блокировку получить нельзя после истечения 300 миллисекунд, возвращается значение false, обозначающее невозможность получения блокировки. Для сравнения, lock.tryLock(30, TimeUnit.SECONDS) ожидает максимум 30 секунд. Вы должны явно указывать оба аргумента.

Класс TimeUnit позволяет указывать единицу времени в секундах, миллисекундах, микросекундах или наносекундах.

1 секунда = 1 тысяча миллисекунд
1 секунда = 1 миллион микросекунд
1 секунда = 1 миллиард наносекунд

Вы уже увидели, как использовать интерфейс Lock, но вы еще не видели, как фактически создать Lock. Во всех предыдущих примерах использовалось:

Lock lock = ...;

Пакет java.util.concurrent.locks включает три реализации интерфейса Lock:

  • ReentrantLock
  • ReentrantReadWriteLock.ReadLock
  • ReentrantReadWriteLock.WriteLock

Обычно используется первая реализация, ReentrantLock. Другие две реализации являются внутренними классами класса ReentrantReadWriteLock. Класс ReentrantReadWriteLock содержит две блокировки: ReadLock и WriteLock.

Типовая схема использования ReentrantLock:

Lock l = new ReentrantLock();
l.lock
();
try {
// доступ к защищенному ресурсу
} finally {
l.unlock();
}

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

ReadWriteLock rwl = new ReentrantReadWriteLock();
Lock readLock = rwl.readLock
();
Lock writeLock = rwl.writeLock
();

Далее вы используете блокировки таким же способом, как и ReentractLock, применяя тип блокировки, соответствующий желаемому типу доступа.

Единственной реализацией интерфейса ReadWriteLock является сам класс theReentrantReadWriteLock.

Дополнительную информацию по поддержке блокировок можно найти в описании пакета.

Теги: java 5

Еще от автора

Применение WeakHashmap для списков слушателей

В статье от 11мая 1999 года Reference Objects были описаны основные идеи применения ссылочных объектов, но не приводилось детального описания. Данная статья позволит вам получить больше сведений, касающихся данной темы. В основном ссылочные объекты применяются для косвенных ссылок на память необходимую объектам. Ссылочные объекты хранятся в очереди (класс ReferenceQueue), в которой отслеживается доступность ссылочных объектов. Исходя из типа ссылочного объекта, сборщик мусора может освобождать память даже тогда, когда обычные ссылки не могут быть освобождены.

Заставки в Mustang

Согласно определению, данному в Wikipedia, заставка - это компьютерный термин, обозначающий рисунок, появляющийся во время загрузки программы или операционной системы. Заставка для пользователя является визуальным отображением инициализации программы. До выхода версии Java SE 6 (кодовое название Mustang) единственной возможностью применения заставки было создание окна, во время запуска метода main, и размещение в нем картинки. Хотя данный способ и работал, но он требовал полной инициализации исполняемой Java среды до появления окна заставки. При инициализации загружались библиотеки AWT и Swing, таким образом, появление заставки задерживалось. В Mustang появился новый аргумент командной строки, значительно облегчающий использование заставок. Этот способ позволяет выводить заставку значительно быстрее до запуска исполняемой Java среды. Окончательное добавление данной функциональности находится на рассмотрении в JCP.

Совмещение изображений

1 Введение 2 Правила визуализации и пример 3 Совмещение изображений в оперативной памяти 4 Постепенное исчезновение изображения 5 Ссылки и дополнительная информация

Еще по теме

Применение WeakHashmap для списков слушателей

В статье от 11мая 1999 года Reference Objects были описаны основные идеи применения ссылочных объектов, но не приводилось детального описания. Данная статья позволит вам получить больше сведений, касающихся данной темы. В основном ссылочные объекты применяются для косвенных ссылок на память необходимую объектам. Ссылочные объекты хранятся в очереди (класс ReferenceQueue), в которой отслеживается доступность ссылочных объектов. Исходя из типа ссылочного объекта, сборщик мусора может освобождать память даже тогда, когда обычные ссылки не могут быть освобождены.

Заставки в Mustang

Согласно определению, данному в Wikipedia, заставка - это компьютерный термин, обозначающий рисунок, появляющийся во время загрузки программы или операционной системы. Заставка для пользователя является визуальным отображением инициализации программы. До выхода версии Java SE 6 (кодовое название Mustang) единственной возможностью применения заставки было создание окна, во время запуска метода main, и размещение в нем картинки. Хотя данный способ и работал, но он требовал полной инициализации исполняемой Java среды до появления окна заставки. При инициализации загружались библиотеки AWT и Swing, таким образом, появление заставки задерживалось. В Mustang появился новый аргумент командной строки, значительно облегчающий использование заставок. Этот способ позволяет выводить заставку значительно быстрее до запуска исполняемой Java среды. Окончательное добавление данной функциональности находится на рассмотрении в JCP.

Использование потоков

1 Введение 2 Работа с выражениями типа Boolean 3 Класс JoptionPane 4 Приложение-счетчик 5 Ссылки

Перехват необрабатываемых исключений

В статье от 16 марта 2004 года Best Practices in Exception Handling были описаны приемы обработки исключений. В данной статье вы изучите новый способ обработки исключений при помощи класса UncaughtExceptionHandler добавленного в J2SE 5.0.

Использование класса LinkedHashMap

1 Введение 2 Сортировка хэш-таблицы 3 Копирование таблицы 4 Сохранение порядка доступа к элементам 5 Ссылки

Сказ про кодировки и java

С кодировками в java плохо. Т.е., наоборот, все идеально хорошо: внутреннее представление строк – Utf16-BE (и поддержка Unicode была с самых первых дней). Все возможные функции умеют преобразовывать строку из маленького регистра в большой, проверять является ли данный символ буквой или цифрой, выполнять поиск в строке (в том числе с регулярными выражениями) и прочее и прочее. Для этих операций не нужно использовать какие-то посторонние библиотеки вроде привычных для php mbstring или iconv. Как говорится, поддержка многоязычных тестов “есть в коробке”. Так откуда берутся проблемы? Проблемы возникают, как только строки текста пытаются “выбраться” из jvm (операции вывода текста различным потребителям) или наоборот пытаются в эту самую jvm “залезть” (операция чтения данных от некоторого поставщика).