(numberTasks);
}
public
Integer getIdStudent() {
return idStudent;
}
public
void setIdStudent(Integer idStudent) {
this.idStudent = idStudent;
}
public
CountDownLatch getCountDownLatch() {
return countDown;
}
ПОТОКИ ВЫПОЛНЕНИЯ
321
public
List getTaskList() {
return taskList;
}
public
void addTask(Task task) {
taskList.add(task);
}
public
void run() {
int i = 0;
for (Task inWork : taskList) {
// на выполнение задания требуется некоторое время
try {
Thread.sleep(new Random().nextInt(100));
}
catch
(InterruptedException e) {
e.printStackTrace();
}
// отправка ответа
inWork.setAnswer("Answer #" + ++i);
System.out.println("Answer #" + i + " from " + idStudent);
}
try {
countDown.await(); // ожидание проверки заданий
} catch (InterruptedException e) {
e.printStackTrace();
}
// подсчет средней оценки за все задачи
float averageMark = 0;
for (Task inWork : taskList) {
// выполнение задания
averageMark += inWork.getMark(); // отправка ответа
}
averageMark /= taskList.size();
System.out.println("Student " + idStudent + ": Average mark = "
+ averageMark);
}
}
// # 27 # поток-тьютор, проверяющий задания # Tutor.java
package
by.bsu.learning;
import
java.util.ArrayList;
import
java.util.List;
import
java.util.Random;
public
class Tutor extends Thread {
private
Integer idTutor;
private
List list;
public
Tutor() {
this.list = new ArrayList<>();
}
public
Tutor(List list) {
this.list = list;
}
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
322
public
Integer getIdTutor() {
return idTutor;
}
public
void setIdTutor(Integer id) {
this.idTutor = id;
}
public
void run() {
for (Student st : list) {
// проверить, выданы ли студенту задания
List tasks = st.getTaskList();
for (Task current : tasks) {
// проверить наличие ответа!
int mark = 3 + new Random().nextInt(7);
current.setMark(mark);
System.out.println(mark + " for student N "
+
st.getIdStudent());
st.getCountDownLatch().countDown();
}
System.out.println("All estimates made for " + st.getIdStudent());
}
}
}
// # 28 # класс-носитель информации # Task.java
package
by.bsu.learning;
public
class Task {
private
String content;
private
String answer;
private
int mark;
public
Task(String content) {
this.content = content;
}
public
String getContent() {
return content;
}
public
void setContent(String content) {
this.content = content;
}
public
String getAnswer() {
return answer;
}
public
void setAnswer(String answer) {
this.answer = answer;
}
public
int getMark() {
return mark;
}
public
void setMark(int mark) {
this.mark = mark;
}
}
ПОТОКИ ВЫПОЛНЕНИЯ
323
/* # 29 # запуск формирования группы студентов и выполнения проверки их заданий #
RunLearning.java */
package
by.bsu.learning;
import
java.util.ArrayList;
public
class RunLearning {
public
static void main(String[] args) {
final int NUMBER_TASKS_1 = 5;
Student student1 = new Student(322801, NUMBER_TASKS_1);
for (int i = 0; i < NUMBER_TASKS_1; i++) {
Task
t
=
new
Task("Task #" + i);
student1.addTask(t);
}
final int NUMBER_TASKS_2 = 4;
Student student2 = new Student(322924, NUMBER_TASKS_2);
for (int i = 0; i < NUMBER_TASKS_2; i++) {
Task
t
=
new
Task("Task ##" + i);
student2.addTask(t);
}
ArrayList lst = new ArrayList();
lst.add(student1);
lst.add(student2);
Tutor tutor = new Tutor(lst);
student1.start();
student2.start();
try { // поток проверки стартует с задержкой
Thread.sleep(5000);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
tutor.start();
}
}
В результате будет выведено:
Answer #1 from 322801
Answer #2 from 322801
Answer #1 from 322924
Answer #2 from 322924
Answer #3 from 322801
Answer #4 from 322801
Answer #5 from 322801
Answer #3 from 322924
Answer #4 from 322924
3 for student N%322801
6 for student N%322801
9 for student N%322801
3 for student N%322801
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
324
6 for student N%322801
All mark for 322801 accepted
9 for student N%322924
8 for student N%322924
3 for student N%322924
9 for student N%322924
All mark for 322924 accepted
Student 322924: Average mark = 7.25
Student 322801: Average mark = 5.4
Следует отметить, что в этом и предыдущих заданиях не полностью выполне-
ны все условия по обеспечению корректной работы приложений во всех возмож-
ных ситуациях, возникающих в условиях многопоточности. Следует рассматри-
вать данный пример в качестве каркаса для построения работоспособного
приложения.
Обмен блокировками
Существует возможность безопасного обмена объектами, в том числе и син-
хронизированными. Функционал обмена представляет класс Exchanger с его
единственным методом T exchange(T ob). Возвращаемый параметр метода —
объект, который будет принят из другого потока, передаваемый параметр ob
метода — собственный объект потока, который будет отдан другому потоку.
Поток Producer представляет информацию о количестве произведенного то-
вара, поток Consumer — о количестве проданного. В результате обмена произ-
водитель снизит план производства, если количество проданного товара меньше
произведенного. Потребитель, к тому же, снижает цену на товар, так как посту-
пления товара больше, чем продано за время, предшествующее обмену.
/* # 30 # содержит Exchanger и представляет основу для производителя и потребителя
# Subject.java */
package
by.bsu.exchanger;
import
java.util.concurrent.Exchanger;
public
class Subject {
protected
static Exchanger- exchanger = new Exchanger<>();
private
String name;
protected
Item item;
public
Subject(String name, Item item) {
this.name = name;
this.item = item;
}
public
String getName() {
return name;
}
ПОТОКИ ВЫПОЛНЕНИЯ
325
public
Item getItem() {
return
item;
}
public
void setName(String name) {
this
.name = name;
}
public
void setItem(Item item) {
this
.item = item;
}
}
/* # 31 # поток-производитель товара, обменивающегося информацией о планах
производства с потребителем # Producer.java */
package
by.bsu.exchanger;
public
class Producer extends Subject implements Runnable {
public
Producer(String name, Item item) {
super
(name, item);
}
public
void run() {
try
{
synchronized
(item) { // блок синхронизации не нужен, но показателен
int
proposedNumber = this.getItem().getNumber();
// обмен синхронизированными экземплярами
item = exchanger.exchange(item);
if
(proposedNumber <= item.getNumber()) {
System.out.println("Producer " + getName()
+ " повышает план производства товара");
else
} {
System.out.println("Producer " + getName()
+ " снижает план производства товара");
}
}
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
/* # 32 # поток-потребитель товара, обменивающийся уровнем продаж
с производителем # Consumer.java */
package
by.bsu.exchanger;
public
class Consumer extends Subject implements Runnable {
public
Consumer(String name, Item item) {
super
(name, item);
}
public
void run() {
try
{
synchronized
(item) { // блок синхронизации не нужен, но показателен
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
326
int
requiredNumber = item.getNumber();
item
=
exchanger.exchange(item); // обмен
if
(requiredNumber >= item.getNumber()) {
System.out.println("Consumer " + getName()
+ " повышает стоимость товара");
else
} {
System.out.println("Consumer " + getName()
+ " снижает стоимость товара");
}
}
catch
} (InterruptedException e) {
e.printStackTrace();
}
}
}
/* # 33 # класс-описание товара # Item.java */
package
by.bsu.exchanger;
public
class Item {
private
Integer id;
private
Integer number;
public
Item(Integer id, Integer number) {
super
();
this
.id = id;
this
.number = number;
}
public
Integer getId() {
return
id;
}
public
Integer getNumber() {
return
number;
}
}
/* # 34 # процесс обмена # RunExchange.java */
package
by.bsu.exchanger;
public
class RunExchange {
public
static void main(String[ ] args) {
Item ss1 = new Item(34, 2200);
Item ss2 = new Item(34, 2100);
new
Thread(new Producer("HP ", ss1)).start();
new
Thread(new Consumer("RETAIL Trade", ss2)).start();
}
}
В результате будет получено:
Consumer RETAIL Trade снижает стоимость товара
Producer HP снижает план производства товара
ПОТОКИ ВЫПОЛНЕНИЯ
327
Альтернатива synchronized
Синхронизация ресурса ключевым словом synchronized накладывает до-
статочно жесткие правила на освобождение этого ресурса. Интерфейс Lock
представляет собой некоторое обобщение синхронизации. Появляется возмож-
ность провести опрос о блокировании, установить время ожидания блокиров-
ки и условия ее прерывания. Интерфейс также оптимизирует работу JVM
с процессами конкурирования за освобождаемые ресурсы.
Класс ReentrantLock представляет два основных метода:
void lock() — получает блокировку экземпляра. Если экземпляр блокирован
другим потоком, то поток отключается и бездействует до освобождения экзем-
пляра;
void unlock() — освобождает блокировку экземпляра. Если текущий по-
ток не является обладателем блокировки, генерируется исключение
IllegalMonitorStateException.
Шаблонное применение этих методов после объявления экземпляра locking
класса ReentrantLock:
locking.lock();
try
{
// some code here
} finally {
locking.unlock();
}
Данная конструкция копирует функциональность блока synchronized.
Гибкость классу предоставляют методы:
boolean tryLock() — получает блокировку экземпляра. Если блокировка
выполнена другим потоком, то метод сразу возвращает false;
boolean tryLock(long timeout, TimeUnit unit) — получает блокировку эк-
земпляра. Если экземпляр блокирован другим потоком, то метод приостанав-
ливает поток на время timeout, и если блокировка становится возможна в тече-
ние этого интервала, то поток ее получает, если же блокировка недоступна,
то метод возвращает false.
Метод lock() и оба метода tryLock() при повторном успешном получении
блокировки в одном и том же потоке увеличивают счетчик блокировок на еди-
ницу, что требует соответствующего числа снятий блокировки.
Класс Condition предназначен для управления блокировкой. Ссылку на эк-
земпляр можно получить только из объекта типа Lock методом newCondition().
Расширение возможностей происходит за счет методов await() и signal(), функ-
циональность которых подобна действию методов wait() и notify() класса Object.
Пусть необходим нереляционный способ сохранения информации в коллек-
ции, когда неделимым квантом информации считается пара или более следую-
щих друг за другом элементов. То есть добавление и удаление элементов может
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
328
осуществляться только парами и другой поток не может добавить/удалить свои
элементы, пока заблокировавший коллекцию поток полностью не выполнит
свои действия.
/* # 35 # ресурсы добавляются и удаляются только парами # DoubleResource.java */
package
by.bsu.lock;
import
java.util.Deque;
import
java.util.LinkedList;
import
java.util.Random;
import
java.util.concurrent.TimeUnit;
import
java.util.concurrent.locks.Condition;
import
java.util.concurrent.locks.Lock;
import
java.util.concurrent.locks.ReentrantLock;
public
class DoubleResource {
private
Deque list = new LinkedList();
private
Lock lock = new ReentrantLock();
private
Condition isFree = lock.newCondition();
public
void adding(String str, int i) {
try {
lock.lock();
list.add(i + "<" + str);
TimeUnit.MILLISECONDS.sleep(new Random().nextInt(50));
list.add(str + ">" + i);
isFree.signal();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
public
String deleting() {
lock.lock();
String s = list.poll();
s += list.poll();
isFree.signal();
lock.unlock();
return s;
}
public
String toString() {
return list.toString();
}
}
/* # 36 # поток доступа к ресурсу # ResThread.java */
package
by.bsu.lock;
import
java.util.Random;
public
class ResThread extends Thread {
private
DoubleResource resource;
public
ResThread(String name, DoubleResource rs) {
ПОТОКИ ВЫПОЛНЕНИЯ
329
super(name);
resource = rs;
}
public
void run() {
for (
int i = 1; i < 4; i++) {
if (
new Random().nextInt(2) > 0) {
resource.adding(getName(),
i);
}
else
{
resource.deleting();
}
}
}
}
/* # 37 # запуск процессов доступа к ресурсу # SynchroMain.java */
package
by.bsu.lock;
import
java.util.concurrent.TimeUnit;
public
class SynchroMain {
public
static void main(String[ ] args)
throws InterruptedException {
for (
int i = 0; i < 5; i++) {
DoubleResource
resource
=
new
DoubleResource();
new ResThread("a", resource).start();
new ResThread("Z", resource).start();
new ResThread("#", resource).start();
TimeUnit.
MILLISECONDS.sleep(1_000);
System.
out.println(resource);
}
}
}
В результате может быть получено:
[11, 22, 11, 1<#, #>1, 33, 3<#, #>3]
[]
[22, 33, 11, 33, 2<#, #>2]
[33, 33, 3<#, #>3]
[11, 22, 33, 3<#, #>3]
где результат с пустыми скобками свидетельствует, что попыток изъятия пар было
больше, чем попыток добавления. Нерезультативные попытки не фиксировались.
ExecutorService и Callable
В альтернативной системе управления потоками разработан механизм ис-
полнителей, функции которого заключаются в запуске отдельных потоков и их
групп, а также в управлении ими: принудительной остановке, контроле числа
работающих потоков и планирования их запуска.
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
330
Класс ExecutorService методом execute(Runnable thread) запускает тради-
ционные потоки, метод же submit(Callable task) запускает потоки с воз-
вращаемым значением. Метод shutdown() останавливает все запущенные им
ранее потоки и прекращает действие самого исполнителя. Статические методы
newSingleThreadExecutor() и newFixedThreadPool(int numThreads) класса
Executors определяют правила, по которым работает ExecutorService, а имен-
но первый позволяет исполнителю запускать только один поток, второй —
не более чем указано в параметре numThreads, ставя другие потоки в очередь
ожидания окончания уже запущенных потоков.
/* # 38 # поток с возвращением результата # CalcCallable.java */
package
by.bsu.future;
import
java.util.Random;
import
java.util.concurrent.Callable;
public
class CalcCallable implements Callable {
@Override
public
Number call() throws Exception {
Number res = new Random().nextGaussian(); // имитация вычислений
return
res;
}
}
/* # 39 # запуск потока и извлечение результата его выполнения # CalcRunner.java */
package
by.bsu.future;
import
java.util.concurrent.ExecutionException;
import
java.util.concurrent.ExecutorService;
import
java.util.concurrent.Executors;
import
java.util.concurrent.Future;
public
class CalcRunner {
public
static void main(String[] args) {
ExecutorService es = Executors.newSingleThreadExecutor();
Future future = es.submit(new CalcCallable());
es.shutdown();
try {
System.out.println(future.get());
}
catch
(InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
Интерфейс Callable представляет поток, возвращающий значение вызываю-
щему потоку. Определяет один метод V call() throws Exception, в код реализации
которого и следует поместить решаемую задачу. Результат выполнения метода
call() может быть получен через экземпляр класса Future, методами V get() или
V
get(long timeout, TimeUnit unit), как и продемонстрировано в предыдущем
ПОТОКИ ВЫПОЛНЕНИЯ
331
примере. Перед извлечением результатов работы потока Callable можно прове-
рить, завершилась ли задача успешно, методами isDone() и isCancelled() соот-
ветственно.
/* # 40 # список обрабатываемых объектов # ProductList.java */
package
by.bsu.future;
import
java.util.ArrayDeque;
public
class ProductList {
private
static ArrayDeque arr = new ArrayDeque() {
{
this.add("Product 1");
this.add("Product 2");
this.add("Product 3");
this.add("Product 4");
this.add("Product 5");
}
};
public
static String getProduct() {
return arr.poll();
}
}
/* # 41 # поток обработки экземпляра продукта # BaseCallable.java */
package
by.bsu.future;
import
java.util.concurrent.Callable;
import
java.util.concurrent.TimeUnit;
public
class BaseCallable implements Callable {
@Override
public
String call() throws Exception {
String product = ProductList.getProduct();
String result = null;
if (product != null) {
result = product + " done";
}
else
{
result = "productList is empty";
}
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(result);
return result;
}
}
/* # 42 # запуск пула потоков и извлечение результатов их работы # RunExecutor.java */
package
by.bsu.future;
import
java.util.ArrayList;
import
java.util.concurrent.ExecutorService;
import
java.util.concurrent.Executors;
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
332
import
java.util.concurrent.Future;
public
class RunExecutor {
public
static void main(String[] args) throws Exception {
ArrayList> list = new ArrayList>();
ExecutorService es = Executors.newFixedThreadPool(3);
for (int i = 0; i < 7; i++) {
list.add(es.submit(new BaseCallable()));
}
es.shutdown();
for (Future future : list) {
System.out.println(future.get() + " result fixed");
}
}
}
В результате выполнения будет, возможно, выведено:
Product 3 done
Product 1 done
Product 2 done
Product 1 done result fixed
Product 3 done result fixed
Product 2 done result fixed
productList is empty
Product 5 done
Product 4 done
Product 4 done result fixed
Product 5 done result fixed
productList is empty result fixed
productList is empty
productList is empty result fixed
Phaser
Более сложное поведение этого синхронизатора Phaser напоминает поведе-
ние CyclicBarrier, однако число участников синхронизации может меняться.
Участвующие стороны сначала должны зарегистрироваться phaser-объектом. Ре-
гистрация осуществляется с помощью методов register(), bulkRegister(int parties)
или подходящего конструктора. Выход из синхронизации phaser-объектом про-
изводит метод arriveAndDeregister(), причем выход из числа синхронизируе-
мых сторон может быть и в случае, когда поток завершил выполнение, и в слу-
чае, когда поток все еще выполняется. Основным назначением класса Phaser
является синхронизация потоков, выполнение которых требуется разбить на от-
дельные этапы (фазы), а эти фазы, в свою очередь, необходимо синхронизо-
вать. Phaser может как задержать поток, пока другие потоки не достигнут конца
ПОТОКИ ВЫПОЛНЕНИЯ
333
текущей фазы методом arriveAndAwaitAdvance(), так и пропустить поток, от-
метив лишь окончание какой-либо фазы методом arrive().
/* # 43 # поток # Truck.java */
package
by.bsu.phaser;
import
java.util.ArrayDeque;
import
java.util.Queue;
import
java.util.Random;
import
java.util.concurrent.Phaser;
public
class Truck implements Runnable {
private
Phaser phaser;
private
String number;
private
int capacity;
private
Storage storafeFrom;
private
Storage storageTo;
private
Queue- bodyStorage;
public
Truck(Phaser phaser, String name, int capacity, Storage stFrom,
Storage
stTo)
{
this.phaser = phaser;
this.number = name;
this.capacity = capacity;
this.bodyStorage = new ArrayDeque- (capacity);
this.storafeFrom = stFrom;
this.storageTo = stTo;
this.phaser.register();
}
public
void run() {
loadTruck();
phaser.arriveAndAwaitAdvance();
goTruck();
phaser.arriveAndAwaitAdvance();
unloadTruck();
phaser.arriveAndDeregister();
}
private
void loadTruck() {
for (int i = 0; i < capacity; i++) {
Item g = storafeFrom.getGood();
if (g == null) { // если в хранилище больше нет товара,
// загрузка грузовика прерывается
return;
}
bodyStorage.add(g);
System.out.println("Грузовик " + number + " загрузил товар №"
+ g.getRegistrationNumber());
try {
Thread.sleep(50);
}
catch
(InterruptedException e) {
e.printStackTrace();
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
334
}
}
}
private
void unloadTruck() {
int size = bodyStorage.size();
for (int i = 0; i < size; i++) {
Item g = bodyStorage.poll();
storageTo.setGood(g);
System.out.println("Грузовик " + number + " разгрузил товар №"
+
g.getRegistrationNumber());
try {
Thread.sleep(50);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
private
void goTruck() {
try {
Thread.sleep(new Random(100).nextInt(500));
System.out.println("Грузовик " + number + " начал поездку.");
Thread.sleep(new Random(100).nextInt(1000));
System.out.println("Грузовик " + number + " завершил поездку.");
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
/* # 44 # перевозимый товар # Item.java */
package
by.bsu.phaser;
public
class Item {
private
int registrationNumber;
public
Item(int number) {
this.registrationNumber = number;
}
public
int getRegistrationNumber() {
return registrationNumber;
}
}
/* # 45 # склад-коллекция # Storage.java */
package
by.bsu.phaser;
import
java.util.Iterator;
import
java.util.List;
import
java.util.Queue;
import
java.util.concurrent.LinkedBlockingQueue;
public
class Storage implements Iterable- {
ПОТОКИ ВЫПОЛНЕНИЯ
335
public
static final int DEFAULT_STORAGE_CAPACITY = 20;
private
Queue- goods = null;
private
Storage() {
goods
=
new
LinkedBlockingQueue- (DEFAULT_STORAGE_CAPACITY);
}
private
Storage(int capacity) {
goods
=
new
LinkedBlockingQueue- (capacity);
}
public
static Storage createStorage(int capacity) {
Storage storage = new Storage(capacity);
return storage;
}
public
static Storage createStorage(int capacity, List- goods) {
Storage storage = new Storage(capacity);
storage.goods.addAll(goods);
return storage;
}
public
Item getGood() {
return goods.poll();
}
public
boolean setGood(Item good) {
return goods.add(good);
}
@Override
public
Iterator- iterator() {
return goods.iterator();
}
}
/* # 46 # запуск процесса # PhaserDemo.java */
package
by.bsu.phaser;
import
java.util.Arrays;
import
java.util.Iterator;
import
java.util.List;
import
java.util.concurrent.Phaser;
public
class PhaserDemo {
public
static void main(String[] args) {
// создание коллекцию товаров
Item[] goods = new Item[20];
for (int i = 0; i < goods.length; i++) {
goods[i]
=
new
Item(i + 1);
}
List- listGood = Arrays.asList(goods);
// создание склада, из которого забирают товары
Storage storageA = Storage.createStorage(listGood.size(), listGood);
// создание склада, куда перевозят товары
Storage storageB = Storage.createStorage(listGood.size());
// создание фазера для синхронизации движения колонны грузовиков
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
336
Phaser phaser = new Phaser();
phaser.register();
int currentPhase;
// создание колонны грузовиков
Thread tr1 = new Thread(new Truck(phaser, "tr1", 5, storageA, storageB));
Thread tr2 = new Thread(new Truck(phaser, "tr2", 6, storageA, storageB));
Thread tr3 = new Thread(new Truck(phaser, "tr3", 7, storageA, storageB));
printGoodsToConsole("Товары на складе A", storageA);
printGoodsToConsole("Товары на складе B", storageB);
// запуск колонны грузовиков на загрузку на одном складе + поездку +
// разгрузку на другом складе
tr1.start();
tr2.start();
tr3.start();
// синхронизация загрузки
currentPhase = phaser.getPhase();
phaser.arriveAndAwaitAdvance();
System.out.println("Загрузка колонны завершена. Фаза " + currentPhase
+ " завершена.");
// синхронизация поездки
currentPhase = phaser.getPhase();
phaser.arriveAndAwaitAdvance();
System.out.println("Поездка колонны завершена. Фаза " + currentPhase
+ " завершена.");
// синхронизация разгрузки
currentPhase = phaser.getPhase();
phaser.arriveAndAwaitAdvance();
System.out.println("Разгрузка колонны завершена. Фаза " + currentPhase
+ " завершена.");
phaser.arriveAndDeregister();
if (phaser.isTerminated()) {
System.out.println("Фазы синхронизированы и завершены.");
}
printGoodsToConsole("Товары на складе A", storageA);
printGoodsToConsole("Товары на складе B", storageB);
}
public
static void printGoodsToConsole(String title, Storage storage) {
System.out.println(title);
Iterator- goodIterator = storage.iterator();
while (goodIterator.hasNext()) {
System.out.print(goodIterator.next().getRegistrationNumber() + " ");
}
System.out.println();
}
}
Товары на складе A
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Товары на складе B
Грузовик tr1 загрузил товар №1
ПОТОКИ ВЫПОЛНЕНИЯ
337
Грузовик tr2 загрузил товар №2
Грузовик tr3 загрузил товар №3
Грузовик tr2 загрузил товар №4
Грузовик tr1 загрузил товар №5
Грузовик tr3 загрузил товар №6
Грузовик tr2 загрузил товар №7
Грузовик tr1 загрузил товар №8
Грузовик tr3 загрузил товар №9
Грузовик tr2 загрузил товар №10
Грузовик tr3 загрузил товар №11
Грузовик tr1 загрузил товар №12
Грузовик tr2 загрузил товар №13
Грузовик tr3 загрузил товар №14
Грузовик tr1 загрузил товар №15
Грузовик tr2 загрузил товар №16
Грузовик tr3 загрузил товар №17
Грузовик tr3 загрузил товар №18
Загрузка колонны завершена. Фаза 0 завершена.
Грузовик tr2 начал поездку.
Грузовик tr1 начал поездку.
Грузовик tr3 начал поездку.
Грузовик tr1 завершил поездку.
Грузовик tr3 завершил поездку.
Грузовик tr2 завершил поездку.
Грузовик tr2 разгрузил товар №2
Грузовик tr1 разгрузил товар №1
Грузовик tr3 разгрузил товар №3
Поездка колонны завершена. Фаза 1 завершена.
Грузовик tr2 разгрузил товар №4
Грузовик tr1 разгрузил товар №5
Грузовик tr3 разгрузил товар №6
Грузовик tr2 разгрузил товар №7
Грузовик tr1 разгрузил товар №8
Грузовик tr3 разгрузил товар №9
Грузовик tr2 разгрузил товар №10
Грузовик tr1 разгрузил товар №12
Грузовик tr3 разгрузил товар №11
Грузовик tr2 разгрузил товар №13
Грузовик tr1 разгрузил товар №15
Грузовик tr3 разгрузил товар №14
Грузовик tr2 разгрузил товар №16
Грузовик tr3 разгрузил товар №17
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
338
Грузовик tr3 разгрузил товар №18
Разгрузка колонны завершена. Фаза 2 завершена.
Фазы синхронизированы и завершены.
Товары на складе A
19 20
Товары на складе B
2 3 1 4 5 6 7 8 9 10 12 11 13 15 14 16 17 18
Задания к главе 11
Вариант А
Разработать многопоточное приложение.
Использовать возможности, предоставляемые пакетом java.util.concurrent.
Не использовать слово synchronized.
Все сущности, желающие получить доступ к ресурсу, должны быть потоками.
Использовать возможности ООП.
Не использовать графический интерфейс. Приложение должно быть кон-
сольным.
1. Порт. Корабли заходят в порт для разгрузки/загрузки контейнеров. Число
контейнеров, находящихся в текущий момент в порту и на корабле, должно
быть неотрицательным и превышающим заданную грузоподъемность суд-
на и вместимость порта. В порту работает несколько причалов. У одного
причала может стоять один корабль. Корабль может загружаться у причала,
разгружаться или выполнять оба действия.
2. Маленькая библиотека. Доступны для чтения несколько книг. Одинаковых
книг в библиотеке нет. Некоторые выдаются на руки, некоторые только
в читальный зал. Читатель может брать на руки и в читальный зал не-
сколько книг.
3. Автостоянка. Доступно несколько машиномест. На одном месте может
находиться только один автомобиль. Если все места заняты, то автомо-
биль не станет ждать больше определенного времени и уедет на другую
стоянку.
4. CallCenter. В организации работает несколько операторов. Оператор может
обслуживать только одного клиента, остальные должны ждать своей очереди.
Клиент может положить трубку и перезвонить еще раз через некоторое время.
5. Автобусные остановки. На маршруте несколько остановок. На одной
остановке может останавливаться несколько автобусов одновременно,
но не более заданного числа.
6. Свободная касса. В ресторане быстрого обслуживания есть несколько
касс. Посетители стоят в очереди в конкретную кассу, но могут перейти
в другую очередь при уменьшении или исчезновении там очереди.
ПОТОКИ ВЫПОЛНЕНИЯ
339
7. Тоннель. В горах существует два железнодорожных тоннеля, по которым
поезда могут двигаться в обоих направлениях. По обоим концам тоннеля
собралось много поездов. Обеспечить безопасное прохождение тоннелей
в обоих направлениях. Поезд можно перенаправить из одного тоннеля
в другой при превышении заданного времени ожидания на проезд.
8. Банк. Имеется банк с кассирами, клиентами и их счетами. Клиент может
снимать/пополнять/переводить/оплачивать/обменивать денежные средст-
ва. Кассир последовательно обслуживает клиентов. Поток-наблюдатель
следит, чтобы в кассах всегда были наличные, при скоплении денег более
определенной суммы, часть их переводится в хранилище, при истощении
запасов наличных происходит пополнение из хранилища.
9. Аукцион. На торги выставляется несколько лотов. Участники аукциона де-
лают заявки. Заявку можно корректировать в сторону увеличения несколь-
ко раз за торги одного лота. Аукцион определяет победителя и переходит
к следующему лоту. Участник, не заплативший за лот в заданный промежу-
ток времени, отстраняется на несколько лотов от торгов.
10. Биржа. На торгах брокеры предлагают акции нескольких фирм. На бирже
совершаются действия по купле-продаже акций. В зависимости от количе-
ства проданных-купленных акций их цена изменяется. Брокеры предлага-
ют к продаже некоторую часть акций. От активности и роста-падения коти-
ровок акций изменяется индекс биржи. Биржа может приостановить торги
при резком падении индекса.
11. Аэропорт. Посадка/высадка пассажиров может осуществляться через конечное
число терминалов и наземным способом через конечное число трапов. Самолеты
бывают разной вместимости и дальности полета. Организовать функционирова-
ние аэропорта, если пунктов назначения 4–6, и зон дальности 2–3.
Вариант B
Для заданий варианта В гл. 4 организовать синхронизированный доступ к ре-
сурсам (файлам). Для каждого процесса создать отдельный поток выполнения.
Тестовые задания к главе 11
Вопрос 11.1.
Дан класс:
class InThread implements Runnable{
public void run() {
System.out.println("running...");
}
}
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
340
Укажите правильные варианты создания потокового объекта (1):
1) new Thread().new InThread();
2) new Runnable(new InThread());
3) new Thread(Intread);
4) new Thread(new InThread());
5) new InThread().
Вопрос 11.2.
Укажите методы, определенные в классе java.lang.Thread (4):
1) join()
2) getPrioroty()
3) wait()
4) notifyAll()
5) sleep()
6) getName()
Вопрос 11.3.
Укажите состояния потока, при вызове на которых метод isAlive() класса
java.lang.Thread вернет значение true (4):
1) NEW
2) RUNNABLE
3) BLOCKED
4) WAITING
5) TIMED_WAITING
6) TERMINATED
Вопрос 11.4.
Дан код:
class InThread implements Runnable{
public void run() {System.out.println("running...");
}
}
public class Quest {
public static void main(String[] args) {
ExecutorService exec = Executors.newFixedThreadPool(2);
exec.execute(new InThread()); exec.execute(new InThread());
exec.execute(new InThread()); exec.execute(new InThread());
exec.execute(new InThread()); exec.execute(new InThread());
exec.shutdown(); while (!exec.isTerminated()) { }
}}
ПОТОКИ ВЫПОЛНЕНИЯ
Сколько потоков выполнит объект exec при запуске этого кода (1)?
1) 2
2) 4
3) 0
4) 6
5) столько, сколько успеет до завершения метода main()
Вопрос 11.5.
Дан класс Lamp(лампочка). Расставьте указанные ниже строки кода метода
turnOn() так, чтобы не допустить ситуации включения лампочки, когда она уже
включена (поле lamp имеет значение true, когда лампочка включена):
class Lamp {
private boolean lamp = false;
public synchronized void turnOn() throws InterruptedException {
________________
}}
a) lamp = true;
b) notify();
c) while (lamp == true) wait();
Выберите один вариант (1):
1) abc;
2) bca;
3) acb;
4) cba;
5) cab.
342
Достарыңызбен бөлісу: