Чтение онлайн

ЖАНРЫ

Философия Java3

Эккель Брюс

Шрифт:

while(workingTellers.size > 1) reassignOneTellerO;

}

// Поручаем кассиру другую работу или отправляем его отдыхать: private void reassignOneTellerO {

Teller teller = workingTellers.pollО;

tel 1 er. doSomethi ngEl seO,

tel1ersDoi ngOtherThi ngs.offer(tel1er);

}

public void runO { try {

while(!Thread.interruptedO) {

TimeUnit.MILLISECONDS.sleep(adjustmentPeriod);

adjustTellerNumberO;

System.out.print(customers +"{");

for(Teller teller workingTellers)

System.out.print(teller.shortString + " "); System.out.printIn("}"); } продолжение &

} catchdnterruptedException

е) {

System.out.printin(this + "прерван");

}

System.out println(this + "завершается");

}

public String toStringO { return "TellerManager "; }

}

public class BankTellerSimulation {

static final int MAX_LINE_SIZE = 50;

static final int ADJUSTMENT_PERIOD = 1000;

public static void main(String[] args) throws exception {

ExecutorService exec = Executors.newCachedThreadPoolО; // Если очередь слишком длинна, клиенты уходят: CustomerLine customers =

new CustomerLi ne(MAX_LINE_SIZE); exec.execute(new CustomerGenerator(customers)); // TellerManager добавляет и убирает кассиров // по мере необходимости: exec.execute(new TellerManager(

exec, customers. ADJUSTMENT_PERIOD)); if(args.length > 0) // Необязательный аргумент

Ti meUni t.SECONDS.s1eep(new Integer(args[0])).

else {

System.out.printIn("Press 'Enter' to quit"); System.in.readО;

}

exec.shutdownNowO;

}

} /* Output:

[429][200][207] { K0 K1 } [861][258][140][322] { K0 K1 } [575][342][804][826][896][984] { КО K1 K2 } [984][810][141][12][689][992][976][368][395][354] { КО K1 K2 КЗ } Teller 2 прерван Teller 2 завершается Teller 1 прерван Teller 1 завершается TellerManager прерван TellerManager завершается Teller 3 прерван Teller 3 завершаетсяч Teller 0 прерван Teller 0 завершается CustomerGenerator прерван CustomerGenerator завершается *///:-

Объекты Customer очень просты; они содержат только поле данных final int. Так как эти объекты никогда не изменяют своего состояния, они являются объектами, доступными только для чтения, и поэтому требуют синхронизации или использования volatile. Вдобавок каждая задача Teller удаляет из очереди ввода только один объект Customer и работает с ним до завершения, поэтому задачи все равно будут работать с Customer последовательно.

Класс CustomerLine представляет собой общую очередь, в которой клиенты ожидают обслуживания. Он реализован в виде очереди ArrayBlockingQueue с методом toString, который выводит результаты в желаемом формате.

Генератор CustomerGenerator присоединяется к CustomerLine и ставит объекты Customer в очередь со случайными интервалами.

Teller извлекает клиентов Customer из CustomerLine и обрабатывает их последовательно, подсчитывая количество клиентов, обслуженных за текущую смену. Если клиентов не хватает, его можно перевести на другую работу (doSome-thingElse), а при появлении большого количества клиентов — снова вернуть на обслуживание очереди методом serveCustomerLine. Чтобы приказать следующему кассиру вернуться к очереди, метод compareTo проверяет количество обслуженных клиентов,

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

Вся основная деятельность выполняется в TellerManager. Этот класс следит за всеми кассирами и за тем, что происходит с клиентами. Одна из интересных особенностей данной имитации заключается в том, что она пытается подобрать оптимальное количество кассиров для заданного потока покупателей. Пример встречается в методе adjustTellerNumber — управляющей системе для надежной, стабильной регулировки количества кассиров. У всех управляющих систем в той или иной мере присутствуют проблемы со стабильностью; слишком быстрая реакция на изменения снижает стабильность, а слишком медленная переводит систему в одно из крайних состояний.

Резюме

В этой главе я постарался изложить основы многопоточного программирования с использованием потоков Java. Прочитав ее, читатель должен понять следующее:

1. Программу можно разделить на несколько независимых задач.

2. Необходимо заранее предусмотреть всевозможные проблемы, возникающие при завершении задач.

3. Задачи, работающие с общими ресурсами, могут мешать друг другу. Основным средством предотвращения конфликтов является блокировка.

4. В неаккуратно спроектированных многозадачных системах возможны взаимные блокировки.

Очень важно понимать, когда рационально использовать параллельное выполнение, а когда этого делать не стоит. Основные причины для его использования:

• управление несколькими подзадачами, одновременное выполнение которых позволяет эффективнее распоряжаться ресурсами компьютера (включая возможность незаметного распределения этих задач по нескольким процессорам);

• улучшенная организация кода;

• удобство для пользователя.

Классический пример распределения ресурсов — использование процессора во время ожидания завершения операций ввода/вывода. Классический пример чуткого пользовательского интерфейса — отслеживание нажатий кнопки «Прервать» во время продолжительного процесса загрузки.

Дополнительным преимуществом потоков является то, что они заменяют «тяжелое» переключение контекста процессов (порядка 1 ООО и более инструкций) «легким» переключением контекста выполнения (около 100 инструкций). Так как все потоки процесса разделяют одно и то же пространство памяти, легкое переключение затрагивает только выполнение программы и локальные переменные. С другой стороны, чередование процессов — тяжелое переключение контекста — требует обновления всего пространства памяти.

Основные недостатки многозадачности:

1. Замедление программы, связанное с ожиданием освобождения блокированных ресурсов.

2. Дополнительная нагрузка на процессор для управления потоками.

3. Совершенно ненужная сложность, являющаяся следствием неудачных решений при проектировании программы.

4. Аномальные ситуации: взаимные блокировки, конфликты доступа, гонки и т. д.

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

Поделиться с друзьями: