Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/com/walking/lesson62_wait_notify/task1/Main.java
Original file line number Diff line number Diff line change
@@ -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;

/**
* Реализуйте имитацию отправки и получения сообщений.
* Один поток должен принимать сообщение, введенное с клавиатуры,
Expand All @@ -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();
}
}
47 changes: 47 additions & 0 deletions src/com/walking/lesson62_wait_notify/task1/model/Message.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.walking.lesson62_wait_notify.task1.model;

public class Message {
private String message;
private boolean isReceived = false;

public Message() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

зачем явно прописывать конструктор по умолчанию?

}

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();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

зачем?

}
}

this.message = message;
isReceived = true;

notifyAll();
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Размещать функциональность отправки и получения сообщения в самом Message - грубая ошибка. Завтра я захочу логику отправки-получения переделать через брокер сообщений или еще как-то - почему я при этом должен менять саму сущность "сообщение"?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

То есть текущий класс надо либо переименовать, либо выделить класс логики, который будет отвечать за обработку сообщения

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Но я бы просто вынес отправку и получение в логику отдельных сущностей - тогда любой потенциальный полиморфизм будет реализовать проще

}
Original file line number Diff line number Diff line change
@@ -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<String> stringConsumer;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Интересная идея. Но не всегда корректная на практике. В данном случае класс, создающий сущность получателя (или отправителя), должен знать, что будет происходить при отправке или получении. Чаще всего это не будет его ответственностью


public MessageReceiver(Message message, Consumer<String> 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();
}
}
}
Original file line number Diff line number Diff line change
@@ -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<String> stringSupplier;

public MessageSender(Message message, Supplier<String> stringSupplier) {
this.message = message;
this.stringSupplier = stringSupplier;
}

@Override
public void run() {
while (!"finish".equalsIgnoreCase(message.getMessage())) {
message.send(stringSupplier.get());
}
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вердикт: технические решения местами интересные, с зонами ответственности классов перемудрил

46 changes: 46 additions & 0 deletions src/com/walking/lesson62_wait_notify/task2/Main.java
Original file line number Diff line number Diff line change
@@ -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;

/**
* Реализуйте имитацию оптовой базы с тремя поставщиками и тремя покупателями.
* Максимальное число хранимых товаров определите на свой вкус.
Expand Down Expand Up @@ -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<Thread> 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<Thread> 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();
}
}
}
37 changes: 37 additions & 0 deletions src/com/walking/lesson62_wait_notify/task2/model/Customer.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
}
92 changes: 92 additions & 0 deletions src/com/walking/lesson62_wait_notify/task2/model/Depot.java
Original file line number Diff line number Diff line change
@@ -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()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не логичнее в методы логгера докрутить varargs и дать возможность передавать параметры для форматирования? Чтобы не форматировать вручную в бизнес-коде

.getName(), count));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

лишний перенос


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;
}
}
}
37 changes: 37 additions & 0 deletions src/com/walking/lesson62_wait_notify/task2/model/Provider.java
Original file line number Diff line number Diff line change
@@ -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()) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

лишний перенос

try {
while (surplus == 0) {
surplus = intSupplier.getAsInt();
}

Thread.sleep(Duration.ofSeconds(random.nextInt(1, 4)));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не помню в условии требований к простою у покупателей или поставщиков


surplus = depot.trySupply(surplus);
} catch (InterruptedException e) {
Thread.currentThread()
.interrupt();
}
}
}
}
Loading