×
Traktatov.net » Философия Java » Читать онлайн
Страница 359 из 395 Настройки

++currentEvenValue; Thread.yieldO; // Ускоряем сбой ++currentEvenValue; return currentEvenValue; } finally { продолжение ^

lock.unlockO,

}

}

public static void main(String[] args) {

EvenChecker test(new MutexEvenGeneratorO).

}

} /// ~

MutexEvenGenerator добавляет мьютекс с именем lock и использует методы lock() и unlock() для создания критической секции в next(). При использовании объектов Lock следует применять идиому, показанную в примере: сразу же за вызовом lock() необходимо разместить конструкцию try-finally, при этом в секцию finally включается вызов unlock() — только так можно гарантировать снятие блокировки.

Хотя try-finally требует большего объема кода, чем ключевое слово synchronized, явное использование объектов Lock обладает своими преимуществами. При возникновении проблем с ключевым словом synchronized происходит исключение, но вы не получите возможность выполнить завершающие действия, чтобы сохранить корректное состояние системы. При работе с объектами Lock можно сделать все необходимое в секции finally.

В общем случае использование synchronized уменьшает объем кода, а также радикально снижает вероятность ошибки со стороны программиста, поэтому явные операции с объектами Lock обычно выполняются только при решении особых задач. Например, с ключевым словом synchronized нельзя попытаться получить блокировку с неудачным исходом или попытаться получить блокировку в течение некоторого промежутка времени с последующим отказом — в подобных случаях приходится использовать библиотеку concurrent:

//: concurrency/AttemptLocking java

// Объекты Lock из библиотеки concurrent делают возможными

// попытки установить блокировку в течение некоторого времени

import java.util.concurrent *;

import java util concurrent.locks.*;

public class AttemptLocking {

private ReentrantLock lock = new ReentrantLockO;

public void untimedO {

boolean captured = lock.tryLockO, try {

System.out printlnCtryLockO: " + captured); } finally {

if(captured)

lock unlockO;

}

}

public void timedO {

boolean captured = false; try {

captured = lock tryLock(2, TimeUnit SECONDS); } catch(InterruptedException e) {

throw new RuntimeException(e);

}

try {

System out println("tryLock(2. TimeUnit SECONDS): " +

captured),

} finally {

if(captured)

lock unlockO,

}

}

public static void main(String[] args) {

final AttemptLocking al = new AttemptLocking(),

al untimedO. // True -- блокировка доступна al timedO. // True -- блокировка доступна // Теперь создаем отдельную задачу для установления блокировки new ThreadO {

{ setDaemon(true), } public void run() {

al lock lockO.

System.out printlnC'acquired");

}

} startO,

Thread yieldO, // Даем возможность 2-й задаче al untimedO; // False -- блокировка захвачена задачей al.timedO. // False -- блокировка захвачена задачей

}

} /* Output-tryLockO. true

tryLock(2, TimeUnit.SECONDS): true acquired

tryLockO false

tryLock(2, TimeUnit SECONDS)- false */// ~

Класс ReentrantLock делает возможной попытку получения блокировки с последующим отказом от нее. Таким образом, если кто-то уже захватил блокировку, вы можете отказаться от своих намерений (вместо того, чтобы дожидаться ее освобождения). В методе timed() делается попытка установления блокировки, которая может завершиться неудачей через 2 секунды (обратите внимание на использование класса Java SE5 TimeUnit для определения единиц времени). В main() отдельный объект Thread создается в виде безымянного класса и устанавливает блокировку, чтобы методы untimed() и timed() могли с чем-то конкурировать.