diff --git a/src/com/walking/lesson62_wait_notify/task1/Main.java b/src/com/walking/lesson62_wait_notify/task1/Main.java index 8f6b92707..596f78bd4 100644 --- a/src/com/walking/lesson62_wait_notify/task1/Main.java +++ b/src/com/walking/lesson62_wait_notify/task1/Main.java @@ -1,5 +1,11 @@ package com.walking.lesson62_wait_notify.task1; +import com.walking.lesson62_wait_notify.task1.model.Message; +import com.walking.lesson62_wait_notify.task1.model.MessageReceiver; +import com.walking.lesson62_wait_notify.task1.model.MessageSender; + +import java.util.Scanner; + /** * Реализуйте имитацию отправки и получения сообщений. * Один поток должен принимать сообщение, введенное с клавиатуры, @@ -8,5 +14,14 @@ */ public class Main { public static void main(String[] args) { + Message message = new Message(); + Scanner scanner = new Scanner(System.in); + Thread sender = new Thread(new MessageSender(message, scanner::nextLine), "messageSender"); + Thread receiver = new Thread(new MessageReceiver(message, System.out::println)); + + System.out.println("Вводите сообщения для отправки. Для завершения работы введите \"finish\"."); + + sender.start(); + receiver.start(); } } \ No newline at end of file diff --git a/src/com/walking/lesson62_wait_notify/task1/model/Message.java b/src/com/walking/lesson62_wait_notify/task1/model/Message.java new file mode 100644 index 000000000..dec843394 --- /dev/null +++ b/src/com/walking/lesson62_wait_notify/task1/model/Message.java @@ -0,0 +1,47 @@ +package com.walking.lesson62_wait_notify.task1.model; + +public class Message { + private String message; + private boolean isReceived = false; + + public Message() { + } + + public String getMessage() { + return message; + } + + public synchronized String receive() { + while (!isReceived) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread() + .interrupt(); + } + } + + isReceived = false; + String receivedMessage = message; + + notifyAll(); + + return receivedMessage; + } + + public synchronized void send(String message) { + while (isReceived) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread() + .interrupt(); + } + } + + this.message = message; + isReceived = true; + + notifyAll(); + } +} diff --git a/src/com/walking/lesson62_wait_notify/task1/model/MessageReceiver.java b/src/com/walking/lesson62_wait_notify/task1/model/MessageReceiver.java new file mode 100644 index 000000000..0cfce2312 --- /dev/null +++ b/src/com/walking/lesson62_wait_notify/task1/model/MessageReceiver.java @@ -0,0 +1,24 @@ +package com.walking.lesson62_wait_notify.task1.model; + +import java.util.function.Consumer; + +public class MessageReceiver implements Runnable { + private final Message message; + private final Consumer stringConsumer; + + public MessageReceiver(Message message, Consumer stringConsumer) { + this.message = message; + this.stringConsumer = stringConsumer; + } + + @Override + public void run() { + String receivedMessage = message.receive(); + + while (!"finish".equalsIgnoreCase(receivedMessage)) { + stringConsumer.accept(receivedMessage); + + receivedMessage = message.receive(); + } + } +} diff --git a/src/com/walking/lesson62_wait_notify/task1/model/MessageSender.java b/src/com/walking/lesson62_wait_notify/task1/model/MessageSender.java new file mode 100644 index 000000000..c90b75e4d --- /dev/null +++ b/src/com/walking/lesson62_wait_notify/task1/model/MessageSender.java @@ -0,0 +1,20 @@ +package com.walking.lesson62_wait_notify.task1.model; + +import java.util.function.Supplier; + +public class MessageSender implements Runnable { + private final Message message; + private final Supplier stringSupplier; + + public MessageSender(Message message, Supplier stringSupplier) { + this.message = message; + this.stringSupplier = stringSupplier; + } + + @Override + public void run() { + while (!"finish".equalsIgnoreCase(message.getMessage())) { + message.send(stringSupplier.get()); + } + } +} diff --git a/src/com/walking/lesson62_wait_notify/task2/Main.java b/src/com/walking/lesson62_wait_notify/task2/Main.java index 3e798fd8e..7daa10e99 100644 --- a/src/com/walking/lesson62_wait_notify/task2/Main.java +++ b/src/com/walking/lesson62_wait_notify/task2/Main.java @@ -1,5 +1,16 @@ package com.walking.lesson62_wait_notify.task2; +import com.walking.lesson62_wait_notify.task2.model.UserListener; +import com.walking.lesson62_wait_notify.task2.model.Customer; +import com.walking.lesson62_wait_notify.task2.model.Depot; +import com.walking.lesson62_wait_notify.task2.model.Provider; +import com.walking.lesson62_wait_notify.task2.util.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.Scanner; + /** * Реализуйте имитацию оптовой базы с тремя поставщиками и тремя покупателями. * Максимальное число хранимых товаров определите на свой вкус. @@ -29,5 +40,40 @@ */ public class Main { public static void main(String[] args) { + Random random = new Random(); + Logger logger = new Logger(); + Depot depot = new Depot(logger); + + List providers = new ArrayList<>(3); + + for (int i = 0; i < 3; i++) { + Thread provider = new Thread(new Provider(depot, () -> random.nextInt(20, 40)), "provider " + (i + 1)); + providers.add(provider); + } + + List customers = new ArrayList<>(3); + + for (int i = 0; i < 3; i++) { + Thread customer = new Thread(new Customer(depot, () -> random.nextInt(20, 30)), "customer " + (i + 1)); + customers.add(customer); + } + + Scanner scanner = new Scanner(System.in); + + UserListener userListener = new UserListener(scanner::nextLine, logger, depot); + userListener.addThread(providers); + userListener.addThread(customers); + + Thread listenerThread = new Thread(userListener, "userListener"); + + listenerThread.start(); + + for (Thread provider : providers) { + provider.start(); + } + + for (Thread customer : customers) { + customer.start(); + } } } \ No newline at end of file diff --git a/src/com/walking/lesson62_wait_notify/task2/model/Customer.java b/src/com/walking/lesson62_wait_notify/task2/model/Customer.java new file mode 100644 index 000000000..cb00fd7d2 --- /dev/null +++ b/src/com/walking/lesson62_wait_notify/task2/model/Customer.java @@ -0,0 +1,37 @@ +package com.walking.lesson62_wait_notify.task2.model; + +import java.time.Duration; +import java.util.Random; +import java.util.function.IntSupplier; + +public class Customer implements Runnable { + private final Depot depot; + private final IntSupplier intSupplier; + private final Random random = new Random(); + + public Customer(Depot depot, IntSupplier intSupplier) { + this.depot = depot; + this.intSupplier = intSupplier; + } + + @Override + public void run() { + int shortage = 1; + + while (!Thread.currentThread() + .isInterrupted()) { + try { + Thread.sleep(Duration.ofSeconds(random.nextInt(1, 4))); + + while (--shortage < 1) { + shortage = intSupplier.getAsInt(); + } + + shortage = depot.tryPurchase(shortage); + } catch (InterruptedException e) { + Thread.currentThread() + .interrupt(); + } + } + } +} diff --git a/src/com/walking/lesson62_wait_notify/task2/model/Depot.java b/src/com/walking/lesson62_wait_notify/task2/model/Depot.java new file mode 100644 index 000000000..200ec1309 --- /dev/null +++ b/src/com/walking/lesson62_wait_notify/task2/model/Depot.java @@ -0,0 +1,92 @@ +package com.walking.lesson62_wait_notify.task2.model; + +import com.walking.lesson62_wait_notify.task2.util.Logger; + +public class Depot { + public final static int MAX_STOCK_VALUE = 100; + public final static int STOP_PURCHASE_STOCK_VALUE = 0; + public final static int STOP_SUPPLY_STOCK_VALUE = 100; + public final static int START_SUPPLY_STOCK_VALUE = 25; + + private final Logger logger; + + private boolean isRequiredSupply = true; + private int stock; + + public Depot(Logger logger) { + this.logger = logger; + } + + public int getStock() { + return stock; + } + + public synchronized int trySupply(int count) { + while (!isRequiredSupply) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread() + .interrupt(); + } + } + + logger.log("Остаток товаров - %d. %s пытается поставить %d товаров.".formatted(stock, Thread.currentThread() + .getName(), count)); + + stock += count; + int surplus = 0; + + inspectCapacity(); + + if (stock > MAX_STOCK_VALUE) { + surplus = stock - MAX_STOCK_VALUE; + stock = MAX_STOCK_VALUE; + + logger.log("Склад заполнен. Избыток товаров возвращен поставщику - %s.".formatted(surplus)); + } + + notifyAll(); + + return surplus; + } + + public synchronized int tryPurchase(int count) { + if (stock == STOP_PURCHASE_STOCK_VALUE) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread() + .interrupt(); + } + } + + logger.log("Остаток товаров - %d. %s пытается купить %d товаров.".formatted(stock, Thread.currentThread() + .getName(), count)); + + int balance = stock - count; + int shortage = 0; + + if (balance >= STOP_PURCHASE_STOCK_VALUE) { + stock = balance; + + inspectCapacity(); + } else { + shortage = count; + + logger.log("Склад пуст. Не хватило товаров для продажи - %s.".formatted(shortage)); + } + + notifyAll(); + + return shortage; + } + + private void inspectCapacity() { + if (stock <= START_SUPPLY_STOCK_VALUE && !isRequiredSupply) { + isRequiredSupply = true; + } else if (stock >= STOP_SUPPLY_STOCK_VALUE && isRequiredSupply) { + isRequiredSupply = false; + } + } +} diff --git a/src/com/walking/lesson62_wait_notify/task2/model/Provider.java b/src/com/walking/lesson62_wait_notify/task2/model/Provider.java new file mode 100644 index 000000000..dd87160eb --- /dev/null +++ b/src/com/walking/lesson62_wait_notify/task2/model/Provider.java @@ -0,0 +1,37 @@ +package com.walking.lesson62_wait_notify.task2.model; + +import java.time.Duration; +import java.util.Random; +import java.util.function.IntSupplier; + +public class Provider implements Runnable { + private final Depot depot; + private final IntSupplier intSupplier; + private final Random random = new Random(); + + public Provider(Depot depot, IntSupplier intSupplier) { + this.depot = depot; + this.intSupplier = intSupplier; + } + + @Override + public void run() { + int surplus = 0; + + while (!Thread.currentThread() + .isInterrupted()) { + try { + while (surplus == 0) { + surplus = intSupplier.getAsInt(); + } + + Thread.sleep(Duration.ofSeconds(random.nextInt(1, 4))); + + surplus = depot.trySupply(surplus); + } catch (InterruptedException e) { + Thread.currentThread() + .interrupt(); + } + } + } +} diff --git a/src/com/walking/lesson62_wait_notify/task2/model/UserListener.java b/src/com/walking/lesson62_wait_notify/task2/model/UserListener.java new file mode 100644 index 000000000..4a05f385a --- /dev/null +++ b/src/com/walking/lesson62_wait_notify/task2/model/UserListener.java @@ -0,0 +1,52 @@ +package com.walking.lesson62_wait_notify.task2.model; + +import com.walking.lesson62_wait_notify.task2.util.Logger; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +public class UserListener implements Runnable { + private final Supplier stringSupplier; + private final List threadList = new ArrayList<>(); + private final Logger logger; + private final Depot depot; + + public UserListener(Supplier stringSupplier, Logger logger, Depot depot) { + this.stringSupplier = stringSupplier; + this.logger = logger; + this.depot = depot; + } + + public void addThread(List threads) { + threadList.addAll(threads); + } + + @Override + public void run() { + while (!Thread.currentThread() + .isInterrupted()) { + try { + Thread.sleep(Duration.ofSeconds(1)); + + if ("finish".equalsIgnoreCase(stringSupplier.get())) { + break; + } + ; + } catch (InterruptedException e) { + Thread.currentThread() + .interrupt(); + } + } + + logger.log("Завешение работы по запросу пользователя."); + + for (Thread thread : threadList) { + logger.log("Поток %s остановлен".formatted(thread)); + thread.interrupt(); + } + + logger.log("Остаток товаров на складе - %d".formatted(depot.getStock())); + } +} diff --git a/src/com/walking/lesson62_wait_notify/task2/util/Logger.java b/src/com/walking/lesson62_wait_notify/task2/util/Logger.java new file mode 100644 index 000000000..7da02a990 --- /dev/null +++ b/src/com/walking/lesson62_wait_notify/task2/util/Logger.java @@ -0,0 +1,7 @@ +package com.walking.lesson62_wait_notify.task2.util; + +public class Logger { + public void log(String message) { + System.out.println(message); + } +} \ No newline at end of file