Лабораторная работа №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)