Лабораторная работа №1.
Расширенные возможности создания многопоточных приложений
Цель работы: 1) научиться использовать расширенные возможности создания многопоточных приложений на Java.
Продолжительность работы: 4 часа.
-
Теоретические сведения
Теоретические сведения и подробное описание очередей и исполнителей можно найти в лекциях и по .адресу: http://java.sun.com/docs/books/tutorial/essential/concurrency/highlevel.html
-
Пример использования очередей
В нижеприведенном примере симулируется ситуация, когда в какое-то хранилище загружается что-то, и одновременно разгружается. В программе представлено два потока (плюс один поток программы). Один поток загружает в хранилище объекты, а второй их оттуда разгружает.
Хранилище имеет ограниченный рамер (константа STORAGE_SIZE). Если места в хранилище нет, погрузчик ожидает освобождения места. С другой стороны, разгрузчик ожидает наличие чего разгружать, если на складе пусто. При окончании загрузки (флаг loadFinished) разгрузчик довыгружает остатки и завершает работу, при этом вместо метода take используется метод poll, т.к.poll в случае если хранилище пусто, не ожидает появления в нем чего-либо, а возвращает null.
Если использовать только take(), то по окончанию загрузки поток-разгрузчик заблокируется навсегда. А если использовать только poll(), то в случае если разгрузка происходит быстрее, чем погрузка, то поток преждевременно сообщит об окончании загрузки и завершит работу, не окончив полностью процесс разгрузки.
public class Queues {
private static final int STORAGE_SIZE = 10;
private static final int GOODS_NUMBER = 30;
private ArrayBlockingQueue<Stuff> storage;
private boolean loadFinished;
public Queues()
{
storage = new ArrayBlockingQueue<Stuff>(STORAGE_SIZE);
}
public void start() {
// запускаем потоки погрузки и разгрузки
Thread loader = new Loader();
loader.start();
Thread unloader = new Unloader();
unloader.start();
try
{
loader.join(); // ждем окончания погрузки
} catch (InterruptedException e)
{
}
loadFinished = true; // устанавливаем флаг, что погрузка закончилась
}
public class Loader extends Thread {
public void run()
{
try {
for (int i = 0; i < GOODS_NUMBER; i++) {
Thread.sleep(new Random().nextInt(300) + 100);
// загружаем новый товар
Stuff stuff = new Stuff(i+1);
storage.put(stuff);
System.out.println(String.format("Товар №%d загружен", stuff.getNumber()));
}
} catch (InterruptedException e) {
}
System.out.println("Загрузка товаров окончена");
}
}
public class Unloader extends Thread {
public void run()
{
try {
while (!isInterrupted()) {
Stuff stuff;
// разгружаем товар
if (loadFinished)
stuff = storage.poll();
else
stuff = storage.take();
if (stuff == null) // разгузка закончилась и в очереди больше ничего нет
break;
Thread.sleep(new Random().nextInt(700) + 200);
System.out.println(String.format("Товар №%d разгружен", stuff.getNumber()));
}
} catch (InterruptedException e) {
}
System.out.println("Разгрузка товаров окончена");
}
}
public class Stuff {
private int number;
/**
* @param number
*/
public Stuff(int number)
{
super();
this.number = number;
}
public int getNumber()
{
return number;
}
}
public static void main(String[] args) {
new Queues().start();
}
}
-
Пример использования исполнителей
Исполнитель принимает на исполнение задачу, которая производится в отдельном потоке. Если у исполнения есть результат, то его можно получить, используя интерфейс Future.
public class Execs {
public static class FactorialCalculator implements Callable<Long> {
private int n;
public FactorialCalculator(int n)
{
super();
this.n = n;
}
@Override
public Long call() throws Exception
{
long result = 1;
for (int i = 1; i < n; i++ ) {
result *= i;
}
return result;
}
}
public static void main(String[] args) {
int n = new Scanner(System.in).nextInt();
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Long> result = executor.submit(new FactorialCalculator(n));
try
{
System.out.println("Результат: " + result.get());
} catch (InterruptedException e)
{
} catch (ExecutionException e)
{
e.printStackTrace();
}
executor.shutdown();
}
}
-
Задания
-
Вариант 1.
-
Реализовать программу, симулирующую процесс работы железнодорожного склада, на котором может храниться один из трех видов товаров: сыпучие, жидкие и скоропортящиеся.
К складу «подъезжает» ж/д состав с определенным количеством и типов вагонов и ожидает погрузку товаров со склада. С другой стороны к складу «подъезжают» автомобили с данными товарами, которые разгружаются на склад. Склад имеет ограниченную вместимость по каждому из видов товаров.
Одновременно может разгружаться только один автомобиль каждого вида. Остальные ожидают своей очереди.
Ж/д состав ожидает полную загрузку всех вагонов, остальные составы ждут своей очереди.
Программа должна иметь графический интерфейс, схематично визуализирующую процесс работы и следующие элементы, управляющие:
-
подъездом автомобиля с грузом пределенного типа
-
подъездом нового ж/д состава.
На экране должно отображаться события:
-
начало разгрузки автомобиля
-
окончания разгрузки автомобиля
-
начало загрузки состава
-
окончание загрузки состава.
Необходимо иметь ввиду, что вместимость вагонов и автомобилей различается.
Реализовать временные задержки для лучшей визуализации процесса.
-
Вариант 2.
Реализовать похожую программу, что и в варианте 1 с одним отличием, что разгрузка происходит в обратном порядке — из ж/д состава в автомобили.
-
Вариант 3.
Реализовать похожую программу, что и в варианте 1 с одним отличием, что в случае полной загрузки скоропортящихся товаров в ж/д состав, его загрузка считается законченной.
-
Вариант 4.
а) Разработать программу-сервер, обрабатывающая запросы от клиентов по сети. Протокол и данные запросов и ответов может быть произвольным. Если обработка данных занимает слишком мало процессорного времени, то для большей наглядности в обработчик запросов необходимо вставить временные задержки.
Программа-сервер должна иметь пул потоков фиксированного размера для обработки запросов от клиента. Программа-сервер может не иметь графического интерфейса.
б) Разработать программу-клиента для тестирования сервера, устанавливающая с сервером одновременно N соединений. Программа должна иметь графический интерфейс, результаты, полученные от сервера должны выводиться пользователю.
В данных программах необходимо использовать исполнителей (Executors)