Эккель Брюс
Шрифт:
//. concurrency/FixedDiningPhilosophers.java
// Обедающие философы без взаимной блокировки.
// {Args: 5 5 timeout}
import java.util.concurrent.*;
public class FixedDiningPhilosophers {
public static void main(String[] args) throws Exception {
int ponder = 5; if (args.length > 0)
ponder = Integer.parselnt(args[0]), int size = 5, if(args.length > 1)
size = Integer parselnt(args[l]); ExecutorService exec = Executors.newCachedThreadPool; Chopstick[] sticks = new Chopstick[size]. for(int i = 0, i < size; i++)
sticks[i] = new ChopstickO; for(int i = 0: i < size; i++) if(i < (size-1))
exec execute(new Philosopher(
sticks[i], sticks[i+l], i, ponder));
else
exec.execute(new Philosopher(
sticksEO], sticksEi], i, ponder)), if(args.length == 3 && argsE2].equals("timeout")) TimeUnit SECONDS.sleep(5);
else {
System out printlnC'Press 'Enter' to quit"); System, in. readO.
}
exec shutdownNow.
}
} ///:-
Проследив за тем, чтобы последний философ брал и откладывал левую палочку раньше правой, мы устраняем взаимную блокировку.
В языке Java нет встроенных средств предупреждения взаимных блокировок; все зависит только от вас и аккуратности вашего кода. Вряд ли эти слова утешат того, кому придется отлаживать программу с взаимной блокировкой.
Новые библиотечные компоненты
В библиотеке java.utiLconcurrent из Java SE5 появился целый ряд новых классов, предназначенных для решения проблем многозадачности. Научившись пользоваться ими, вы сможете создавать более простые и надежные многозадачные программы.
В этом разделе приведено немало примеров использования различных компонентов. Другие, относительно редко встречающиеся компоненты, здесь не рассматриваются.
Так как компоненты предназначены для решения разных проблем, простого способа их упорядочения не существует, поэтому мы начнем с более простых примеров и постепенно перейдем к более сложным.
CountDownLatch
Класс синхронизирует задачи, заставляя их ожидать завершения группы операций, выполняемых другими задачами.
Объекту CountDownLatch присваивается начальное значение счетчика, а все задачи, вызвавшие await для этого объекта, блокируются до момента обнуления счетчика. Другие задачи могут уменьшать счетчик, вызывая метод countDown для объекта (обычно это делается тогда, когда задача завершает свою работу). Класс CountDownLatch рассчитан на «одноразовое» применение; счетчик не может возвращаться к прежнему состоянию. Если вам нужна версия с возможностью сброса счетчика, воспользуйтесь классом CyclicBarrier.
Задачи, вызывающие countDown, не блокируются на время вызова. Только вызов await блокируется до момента обнуления счетчика.
Типичный способ применения — разделение задачи на п независимых подзадач и создание объекта CountDownLatch с начальным значением п. При завершении каждая подзадача вызывает countDown для объекта синхронизации. Потоки, ожидающие решения общей задачи, блокируются вызовом await. Описанная методика продемонстрирована в следующем примере:
// concurrency/CountDownLatchDemo java import java.util concurrent.*; import java util *.
import static net mindview util.Print *,
// ЧАсть основной задачи.
class TaskPortion implements Runnable {
private static int counter = 0.
private final int id = counter++;
private static Random rand = new Random(47);
private final CountDownLatch latch;
TaskPortion(CountDownLatch latch) { this latch = latch,
}
public void run { try {
doWorkO;
latch countDownO; } catchdnterruptedException ex) {
// Приемлемый вариант выхода
}
}
public void doWorkO throws InterruptedException {
TimeUnit MILLISECONDS.sleep(rand nextlnt(2000)); pri nt(thi s + "завершается");
}
public String toStringO {
return String.format("^l$-3d ". id),
// Ожидание по объекту CountDownLatch: class WaitingTask implements Runnable { private static int counter = 0; private final int id = counter++; private final CountDownLatch latch, WaitingTask(CountDownLatch latch) { this latch = latch;
public void run { try {
latch awaitO.
printC'Bapbep пройден для " + this); } catchdnterruptedException ex) {
print(this + " interrupted"),
}
}
public String toStringO {
return String format("WaitingTask %l%-36 id).
public class CountDownLatchDemo { static final int SIZE = 100.
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPoolО, // Все подзадачи совместно используют один объект CountDownLatch CountDownLatch latch = new CountDownLatch(SIZE); for(int i = 0; i < 10; i++)