Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Готовые отчеты (2020) / Java. Лабораторная работа 7

.pdf
Скачиваний:
59
Добавлен:
29.01.2021
Размер:
1.04 Mб
Скачать

}

public double get(int i) throws VectorIndexOutOfBoundsException { return vector.get(i);

}

public int size() { return vector.size();

}

protected final IVector vector;

}

// Некоторый класс с многопоточной логикой; наследуется от Keeper public static class SomeConcurrentKeeper extends Keeper {

public SomeConcurrentKeeper(IVector vector, int maxPermits) { super(vector);

this.index = -1; this.current = 0; this.maxPermits = maxPermits;

}

public synchronized void set(int i, double d) throws VectorIndexOutOfBoundsException {

try {

while (current > 0) this.wait();

}

catch (Exception e) { e.printStackTrace();

}

vector.set(i, d); current = maxPermits; index = i; this.notifyAll();

}

public synchronized double get(int i) throws VectorIndexOutOfBoundsException {

try {

while (index != i) this.wait();

}

catch (Exception e) { e.printStackTrace();

}

double x = vector.get(i); --current; this.notifyAll();

return x;

}

public synchronized int size() { return vector.size();

}

private volatile int current, index; private final int maxPermits;

}

// Класс для последовательной записи значений в вектор public static class VectorWriterRun implements Runnable {

public VectorWriterRun(Keeper keeper) { this.keeper = keeper;

}

public void run() { try {

Thread current = Thread.currentThread();

for (int i = 0, size = keeper.size(); i < size; ++i) { double d = Math.random();

keeper.set(i, d); // Простая запись System.out.println("(" + current.getName() + ") Write: "

+ d + " to position " + i);

}

11

}

catch(Exception e) { e.printStackTrace();

}

}

private final Keeper keeper;

}

// Класс для последовательного считывания значений из вектора public static class VectorReaderRun implements Runnable {

public VectorReaderRun(Keeper keeper) { this.keeper = keeper;

}

public void run() { try{

Thread current = Thread.currentThread();

for (int i = 0, size = keeper.size(); i < size; ++i) { System.out.println("- (" + current.getName() + ") Read: "

+ keeper.get(i) + " from position " + i); // Простое чтение

}

}

catch(Exception e) { e.printStackTrace();

}

}

private final Keeper keeper;

}

}

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

Рисунок 4 — Компиляция и запуск Task7Part2SRP.java

SRP — Single Responsibility Principle (принцип единственной ответственности).

В первом случае механизм потоков не используется. Вместо этого сначала происходит полная запись значений в вектор, затем чтение этих значений. Оба процесса выполняются в одном потоке — main. Этот однопоточный вариант быстрее тогда, когда предполагается использование

12

ровно одного VectorReaderRun, так как не требуется время на постоянное переключение с потока записи на поток чтения (и обратно).

Во втором случае используется механизм потоков. Имеем три потока чтения. Причем информация, которая выводится на экран, не синхронизирована по причине отсутствия логики синхронизации при выводе в VectorWriterRun и VectorReaderRun. Зато выводимые значения верны, так как методы set и get класса ConcurrentVector синхронизированы.

Задание №3.

Добавим в класс со статическими методами обработки векторов

реализацию метода IVector synchronizedVector(IVector vector),

возвращающего ссылку на оболочку (паттерн проектирования «Декоратор») указанного вектора, безопасную с точки зрения многопоточности (табл. 5). Таблица 5 — Фрагмент измененного кода vectors/Vectors.java

// ...

public class Vectors {

//...

//Метод, возвращающий безопасный с точки зрения многопоточности вектор public static IVector concurrentVector(IVector vector) {

return new ConcurrentVector(vector);

}

//...

}

Реализуем конкретный потокобезопасный класс-декоратор ConcurrentVector, используя встроенный в Java 1.5 механизм блокировок (табл. 6).

Таблица 6 — Код vectors/ConcurrentVector.java

package vectors;

import java.util.Iterator;

import java.util.concurrent.locks.ReentrantLock;

public class ConcurrentVector implements IVector { // Конструктор

public ConcurrentVector(IVector arg) { this.vector = arg;

this.lock = new ReentrantLock();

}

// Получение элемента по индексу

public double get(int i) throws VectorIndexOutOfBoundsException { lock.lock();

double x = this.vector.get(i); lock.unlock();

return x;

}

13

// Изменение значения элемента по индексу public void set(int i, double value) throws

VectorIndexOutOfBoundsException { lock.lock(); this.vector.set(i, value); lock.unlock();

}

//Получение длины вектора public int size() {

lock.lock();

int size = this.vector.size(); lock.unlock();

return size;

}

//Нахождение нормы Евклида public double normEuclidean() {

lock.lock();

double norm = this.vector.normEuclidean(); lock.unlock();

return norm;

}

//Вывод всех элементов вектора

public void print() { lock.lock(); this.vector.print(); lock.unlock();

}

// Получение итератора public Iterator iterator() {

lock.lock();

Iterator it = this.vector.iterator(); lock.unlock();

return it;

}

protected IVector vector; protected ReentrantLock lock;

}

Создадим файл ConcurrentVectorTest.java для проверки работы метода

concurrentVector (табл. 7).

Таблица 7 — Код ConcurrentVectorTest.java

import vectors.Array; import vectors.Vectors; import vectors.IVector;

public class ConcurrentVectorTest {

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

IVector concurrentVector = Vectors.concurrentVector(new Array(8)); IVector nonConcurrentVector = new Array(8); System.out.print("Concurrent vector: "); concurrentVector.print();

System.out.print("Non-concurrent vector: "); nonConcurrentVector.print();

System.out.println("[Testing 'print' method of the concurrent vector]");

VectorPrinter vectorPrinter = new VectorPrinter(concurrentVector); Thread thread1 = new Thread(vectorPrinter);

14

Thread thread2 = new Thread(vectorPrinter); Thread thread3 = new Thread(vectorPrinter); thread1.start();

thread2.start();

thread3.start();

thread1.join();

thread2.join();

thread3.join();

System.out.println("[Testing 'print' method of the non-concurrent vector]");

vectorPrinter = new VectorPrinter(nonConcurrentVector); thread1 = new Thread(vectorPrinter);

thread2 = new Thread(vectorPrinter); thread3 = new Thread(vectorPrinter); thread1.start();

thread2.start();

thread3.start();

thread1.join();

thread2.join();

thread3.join();

}

public static class VectorPrinter implements Runnable { public VectorPrinter(IVector vector) {

this.vector = vector;

}

public void run() { vector.print();

}

IVector vector;

}

}

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

Рисунок 5 — Компиляция и запуск ConcurrentVectorTest.java

Впервом (потокобезопасном) варианте вектор был выведен по одному разу каждым из трех потоков синхронизировано. Во втором (непотокобезопасном) варианте произошла гонка потоков (академ. «неопределённость параллелизма»).

Вданном случае мы использовали стандартную реализацию

блокировки java.util.concurrent.locks.ReentrantLock. Хотя на самом деле не

так сложно сделать свою собственную более простую по желанию (табл. 8).

15

Таблица 8 — Код vectors/Lock.java package vectors;

public class Lock {

public synchronized void lock() { try {

while (lock) this.wait();

}

catch(Exception e) { e.printStackTrace();

}

lock = true;

callingThread = Thread.currentThread();

}

public synchronized void unlock() {

Thread currentThread = Thread.currentThread(); if (lock && callingThread == currentThread) {

lock = false; this.notifyAll();

}

else if (callingThread != currentThread) { throw new IllegalMonitorStateException();

}

}

public synchronized boolean isLocked() { return lock;

}

public synchronized Thread getOwner() { return callingThread;

}

boolean lock = false; Thread callingThread = null;

}

Вфайле vectors/ConcurrentVector.java удалим строку import

java.util.concurrent.locks.ReentrantLock; и все вхождения ReentrantLock

заменим на Lock.

Результат компиляции и запуска ConcurrentVectorTest.java приведен на рис. 6.

Рисунок 6 — Компиляция и запуск ConcurrentVectorTest.java Заключение

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

16