русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Public synchronized


Дата добавления: 2015-06-12; просмотров: 680; Нарушение авторских прав


void take() throws InterruptedException {

while(taken)

wait();

taken = true;

}

public synchronized void drop() {

taken = false;

notifyAll();

}

}

Два философа (Philosopher) ни при каких условиях не смогут успешно взять (take()) одну и ту же палочку (Chopstick) одновременно. Если один философ уже взял палочку, другому философу придется подождать (wait()), пока она не будет освобождена текущим пользователем (drop()).

Когда задача Philosopher вызывает take(), она ожидает, пока флаг taken не перейдет в состояние false (то есть пока палочка не будет освобождена тем философом, который держит ее в данный момент). Далее задача устанавливает флаг taken равным true, показывая тем самым, что палочка занята. Завершив работу с Chopstick, Philosopher вызывает drop(), чтобы изменить флаг и оповестить (notifyAll()) всех остальных философов, ожидающих освобождения палочки:

//: concurrency/Philosopher.java

// Обедающий философ

import java.util.concurrent.*;

import java.util.*;

import static net.mindview.util.Print.*;

 

public class Philosopher implements Runnable {

private Chopstick left;

private Chopstick right;

private final int id;

private final int ponderFactor;

private Random rand = new Random(47);

private void pause() throws InterruptedException {

if(ponderFactor == 0) return;

TimeUnit.MILLISECONDS.sleep(

rand.nextInt(ponderFactor * 250));

}

public Philosopher(Chopstick left, Chopstick right,

int ident, int ponder) {

this.left = left;

this.right = right;

id = ident;

ponderFactor = ponder;

}

public void run() {

try {

while(!Thread.interrupted()) {

print(this + " " + "thinking");

pause();

// Философ проголодался

print(this + " " + "grabbing right");

right.take();



print(this + " " + "grabbing left");

left.take();

print(this + " " + "eating");

pause();

right.drop();

left.drop();

}

} catch(InterruptedException e) {

print(this + " " + "exiting via interrupt");

}

}

public String toString() { return "Philosopher " + id; }

}

В методе Philosopher.run() все философы непрерывно переходят от размышлений к еде, и наоборот. Метод pause() делает паузу случайной продолжительности, если значение ponderFactor отлично от нуля. Итак, Philosopher думает в течение случайного промежутка времени, затем пытается захватить левую и правую палочки вызовами take(), ест в течение случайного промежутка времени, а затем все повторяется.

В следующей версии программы возникает взаимная блокировка:

//: concurrency/DeadlockingDiningPhilosophers.java

// Демонстрация скрытой возможности взаимной блокировки.

// {Args: 0 5 timeout}

import java.util.concurrent.*;

 

public class DeadlockingDiningPhilosophers {

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

int ponder = 5;

if(args.length > 0)

ponder = Integer.parseInt(args[0]);

int size = 5;

if(args.length > 1)

size = Integer.parseInt(args[1]);

ExecutorService exec = Executors.newCachedThreadPool();

Chopstick[] sticks = new Chopstick[size];

for(int i = 0; i < size; i++)

sticks[i] = new Chopstick();

for(int i = 0; i < size; i++)

exec.execute(new Philosopher(

sticks[i], sticks[(i+1) % size], i, ponder));

if(args.length == 3 && args[2].equals("timeout"))

TimeUnit.SECONDS.sleep(5);

else {

System.out.println("Press 'Enter' to quit");

System.in.read();

}

exec.shutdownNow();

}

} /* (Execute to see output) *///:~

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

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

Объектам Chopstick не нужны внутренние идентификаторы; они идентифицируются по своей позиции в массиве sticks. Каждому конструктору Philosopher передаются ссылки на правую и левую палочки Chopstick. Последнему Philosopher в качестве правой палочки передается нулевой объект Chopstick; круг замыкается. Теперь может возникнуть ситуация, когда все философы одновременно попытаются есть, и каждый из них будет ожидать, пока сосед положит свою палочку. В программе наступает взаимная блокировка.

Если философы тратят на размышления больше времени, чем на еду, вероятность взаимной блокировки значительно снижается. Даже может возникнуть иллюзия, что программа свободна от блокировок (при ненулевом значении ponder или большом количестве объектовPhilosopher), хотя на самом деле это не так. Именно этим и интересен настоящий пример: программа вроде бы ведет себя верно, тогда как на самом деле возможна взаимная блокировка.

Для решения проблемы необходимо осознавать, что тупик имеет место при стечении следующих четырех обстоятельств:

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

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

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

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

Так как взаимная блокировка возникает лишь при соблюдении всех перечисленных условий, для упреждения тупика достаточно нарушить всего лишь одно из них. В нашей программе проще всего нарушить четвертое условие: оно выполняется, поскольку каждый философ старается брать палочки в определенном порядке — сначала левую, потом правую. Из-за этого может возникнуть ситуация, когда каждый из них держит свою левую палочку и ждет освобождения правой, что и приводит к циклическому ожиданию. Если инициализировать последнего философа так, чтобы он сначала пытался взять левую палочку, а потом правую, взаимная блокировка станет невозможна. Это всего лишь одно решение проблемы, но вы можете предотвратить ее, нарушив одно из оставшихся условий (за подробностями обращайтесь к специализированной литературе по многозадачному программированию):

//: 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.parseInt(args[0]);

int size = 5;

if(args.length > 1)

size = Integer.parseInt(args[1]);

ExecutorService exec = Executors.newCachedThreadPool();

Chopstick[] sticks = new Chopstick[size];

for(int i = 0; i < size; i++)

sticks[i] = new Chopstick();

for(int i = 0; i < size; i++)

if(i < (size-1))

exec.execute(new Philosopher(

sticks[i], sticks[i+1], i, ponder));

Else

exec.execute(new Philosopher(

sticks[0], sticks[i], i, ponder));

if(args.length == 3 && args[2].equals("timeout"))

TimeUnit.SECONDS.sleep(5);

else {

System.out.println("Press 'Enter' to quit");

System.in.read();

}

exec.shutdownNow();

}

} /* (Execute to see output) *///:~

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

В языке Java нет встроенных средств предупреждения взаимных блокировок; все зависит только от вас и аккуратности вашего кода. Вряд ли эти слова утешат того, кому придется отлаживать программу с взаимной блокировкой.

 

Новые библиотечные компоненты

В библиотеке java.util.concurrent из Java SE5 появился целый ряд новых классов, предназначенных для решения проблем многозадачности. Научившись пользоваться ими, вы сможете создавать более простые и надежные многозадачные программы.

В этом разделе приведено немало примеров использования различных компонентов. Другие, относительно редко встречающиеся компоненты, здесь не рассматриваются.

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



<== предыдущая лекция | следующая лекция ==>
Взаимная блокировка | CountDownLatch


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.215 сек.