Семафоры
При обычной блокировке доступ к ресурсу в любой момент времени разрешается только одной задаче. Семафор со счетчиком позволяет п задачам одновременно* обращаться к ресурсу. Можно считать, что семафор «выдает разрешения» на использование ресурса, хотя никаких реальных объектов разрешений в этой схеме нет.
В качестве примера рассмотрим концепцию пула объектов: объекты, входящие в пул, «выдаются» для использования, а затем снова возвращаются в пул после того, как пользователь закончит работу с ними. Эта функциональность инкапсулируется в параметризованном классе:
// concurrency/Pool java
// Использование Semaphore в Pool ограничивает количество // задач, которые могут использовать ресурс import java util concurrent *. import java util *,
public class Pool
private List
// Предполагается наличие конструктора по умолчанию items add(classObject newInstanceO). } catch(Exception e) {
throw new RuntimeException(e);
}
}
public T checkout О throws InterruptedException { available acquireO; return getltemO,
}
public void checkIn(T x) { if(releaseltem(x))
available releasee);
}
private synchronized T getltemO {
for(int i =0; i < size, ++i) if(!checkedOut[i]) {
checkedOut[i] = true, return items get(i);
}
return null. // Семафор предотвращает переход в зту точку
}
private synchronized boolean releaseItem(T item) { int index = items indexOf(item). if(index == -1) return false; // Отсутствует в списке if(checkedOut[index]) {
checkedOut[index] = false, return true,
}
return false; // He был освобожден
}
В этой упрощенной форме конструктор использует newlnstance() для заполнения пула объектами. Если вам понадобится новый объект, вызовите check-Out(); завершив работу с объектом, передайте его checkln().
Логический массив checkedOut отслеживает выданные объекты. Для управления его содержимым используются методы getltem() и releaseltem(). В свою очередь, эти методы защищены семафором available, поэтому в checkOut() семафор available блокирует дальнейшее выполнение при отсутствии семафорных разрешений (то есть при отсутствии объектов в пуле). Метод checkln() проверяет действительность возвращаемого объекта, и, если объект действителен, разрешение возвращается семафору.
Для примера мы воспользуемся классом Fat. Создание объектов этого класса является высокозатратной операцией, а на выполнение конструктора уходит много времени:
//: concurrency/Fat java
// Объекты, создание которых занимает много времени
public class Fat {
private volatile double d. // Предотвращает оптимизацию private static int counter = 0. private final int id = counter++. public FatО {
// Затратная, прервываемая операция for(int i = 1: i < 10000; i++) {
d += (Math PI + Math.E) / (double)i.
}
}
public void operationO { System out println(this); } public String toStringO { return "Fat id: " + id; } } ///:-