From ed02a2906b4ead0874e72aa0eed078fc0af52b47 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 07:29:00 +0000 Subject: [PATCH 01/10] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d7a6ba3..e974d43 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/qcWcnElX) # Java concurrency # Цели и задачи л/р: From c1004dd6b8084de6b9fe9bb57488d11d82da0eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=94=D0=B0=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=BE=D0=B2?= Date: Tue, 30 Sep 2025 22:57:45 +0300 Subject: [PATCH 02/10] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B2=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/labs/App.java | 29 ++++++++++++++ src/main/java/org/labs/Bowl.java | 27 +++++++++++++ src/main/java/org/labs/Constants.java | 9 +++++ src/main/java/org/labs/Main.java | 7 ---- src/main/java/org/labs/Programmer.java | 52 ++++++++++++++++++++++++++ src/main/java/org/labs/Spoon.java | 8 ++++ src/main/java/org/labs/Waiter.java | 32 ++++++++++++++++ 7 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/labs/App.java create mode 100644 src/main/java/org/labs/Bowl.java create mode 100644 src/main/java/org/labs/Constants.java delete mode 100644 src/main/java/org/labs/Main.java create mode 100644 src/main/java/org/labs/Programmer.java create mode 100644 src/main/java/org/labs/Spoon.java create mode 100644 src/main/java/org/labs/Waiter.java diff --git a/src/main/java/org/labs/App.java b/src/main/java/org/labs/App.java new file mode 100644 index 0000000..d41e9e0 --- /dev/null +++ b/src/main/java/org/labs/App.java @@ -0,0 +1,29 @@ +package org.labs; + +import java.util.ArrayList; +import java.util.stream.Stream; + +public class App { + public static void main(String[] args) { + final var waiter = new Waiter(); + final var spoons = Stream.generate(Spoon::new).limit(Constants.PROGRAMMERS_COUNT).toList(); + + final var programmers = new ArrayList(); + for (Integer i = 0; i < Constants.PROGRAMMERS_COUNT; i++) { + Integer leftSpoonIdx = i == Constants.PROGRAMMERS_COUNT - 1 ? 0 : i; + Integer rightSpoonIdx = i == 0 ? Constants.PROGRAMMERS_COUNT - 1 : i - 1; + + programmers.add(new Programmer(i, waiter, spoons.get(leftSpoonIdx), spoons.get(rightSpoonIdx))); + } + + for (var p : programmers) + p.start(); + + for (var p : programmers) { + try { + p.join(); + } catch (InterruptedException e) { + } + } + } +} \ No newline at end of file diff --git a/src/main/java/org/labs/Bowl.java b/src/main/java/org/labs/Bowl.java new file mode 100644 index 0000000..b86730c --- /dev/null +++ b/src/main/java/org/labs/Bowl.java @@ -0,0 +1,27 @@ +package org.labs; + +public class Bowl { + private Integer foodUnits; + + Bowl() { + this(0); + } + + Bowl(Integer foodUnits) { + this.foodUnits = foodUnits; + } + + public Integer getFoodUnits() { + return foodUnits; + } + + public Integer eat(Integer bite) { + Integer bitten = foodUnits < bite ? foodUnits : bite; + foodUnits -= bitten; + return bitten; + } + + public Boolean isEmpty() { + return foodUnits == 0; + } +} diff --git a/src/main/java/org/labs/Constants.java b/src/main/java/org/labs/Constants.java new file mode 100644 index 0000000..a19ed9b --- /dev/null +++ b/src/main/java/org/labs/Constants.java @@ -0,0 +1,9 @@ +package org.labs; + +public class Constants { + public static final Integer PORTION_SIZE = 10; + public static final Integer BITE_SIZE = 10; + public static final Integer PORTIONS_COUNT = 1_000_000; + + public static final Integer PROGRAMMERS_COUNT = 7; +} diff --git a/src/main/java/org/labs/Main.java b/src/main/java/org/labs/Main.java deleted file mode 100644 index 9917247..0000000 --- a/src/main/java/org/labs/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.labs; - -public class Main { - public static void main(String[] args) { - System.out.println("Hello, World!"); - } -} \ No newline at end of file diff --git a/src/main/java/org/labs/Programmer.java b/src/main/java/org/labs/Programmer.java new file mode 100644 index 0000000..758e9c5 --- /dev/null +++ b/src/main/java/org/labs/Programmer.java @@ -0,0 +1,52 @@ +package org.labs; + +public class Programmer extends Thread { + private final Integer id; + + private final Integer biteSize; + + private Integer eaten = 0; + private Bowl bowl = new Bowl(); + + private final Waiter waiter; + private final Spoon leftSpoon; + private final Spoon rightSpoon; + + Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon) { + this(id, waiter, leftSpoon, rightSpoon, Constants.BITE_SIZE); + } + + Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon, Integer biteSize) { + this.id = id; + this.waiter = waiter; + this.leftSpoon = leftSpoon; + this.rightSpoon = rightSpoon; + this.biteSize = biteSize; + + } + + @Override + public void run() { + while (true) { + if (!waiter.hasMoreFood()) { + break; + } + + if (bowl.isEmpty()) { + try { + bowl = waiter.pickBowl(); + } catch (Exception e) { + break; + } + } + + if (leftSpoon.lock.tryLock() && rightSpoon.lock.tryLock()) { + Integer bitten = bowl.eat(biteSize); + eaten += bitten; + + leftSpoon.lock.unlock(); + rightSpoon.lock.unlock(); + } + } + } +} diff --git a/src/main/java/org/labs/Spoon.java b/src/main/java/org/labs/Spoon.java new file mode 100644 index 0000000..4ff4351 --- /dev/null +++ b/src/main/java/org/labs/Spoon.java @@ -0,0 +1,8 @@ +package org.labs; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class Spoon { + public Lock lock = new ReentrantLock(); +} diff --git a/src/main/java/org/labs/Waiter.java b/src/main/java/org/labs/Waiter.java new file mode 100644 index 0000000..ba85592 --- /dev/null +++ b/src/main/java/org/labs/Waiter.java @@ -0,0 +1,32 @@ +package org.labs; + +public class Waiter { + private Integer bowlsCount; + private Integer portionSize; + + Waiter() { + this(Constants.PORTIONS_COUNT); + } + + Waiter(Integer bowlsCount) { + this(bowlsCount, Constants.PORTION_SIZE); + } + + Waiter(Integer bowlsCount, Integer portionSize) { + this.bowlsCount = bowlsCount; + this.portionSize = portionSize; + } + + synchronized Boolean hasMoreFood() { + return bowlsCount > 0; + } + + synchronized public Bowl pickBowl() throws Exception { + if (bowlsCount == 0) { + throw new Exception("No more portions left"); + } + + bowlsCount--; + return new Bowl(portionSize); + } +} From 056cef375e4fffb7e108be65a01d72359b0a52fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=94=D0=B0=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=BE=D0=B2?= Date: Fri, 3 Oct 2025 13:46:57 +0300 Subject: [PATCH 03/10] =?UTF-8?q?=D0=A7=D0=B5=D1=80=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BE=D0=B9=20=D0=B2=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 +- src/main/java/org/labs/App.java | 11 ++- src/main/java/org/labs/Constants.java | 4 +- src/main/java/org/labs/Programmer.java | 68 +++++++++++++------ src/main/java/org/labs/Spoon.java | 10 +++ src/main/java/org/labs/Waiter.java | 32 --------- .../java/org/labs/loggers/ConsoleLogger.java | 8 +++ src/main/java/org/labs/loggers/Logger.java | 5 ++ .../java/org/labs/{ => waiters}/Bowl.java | 6 +- src/main/java/org/labs/waiters/Waiter.java | 41 +++++++++++ 10 files changed, 131 insertions(+), 59 deletions(-) delete mode 100644 src/main/java/org/labs/Waiter.java create mode 100644 src/main/java/org/labs/loggers/ConsoleLogger.java create mode 100644 src/main/java/org/labs/loggers/Logger.java rename src/main/java/org/labs/{ => waiters}/Bowl.java (83%) create mode 100644 src/main/java/org/labs/waiters/Waiter.java diff --git a/.gitignore b/.gitignore index b63da45..d9c5ded 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,7 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store + +### Log files ### +*.log \ No newline at end of file diff --git a/src/main/java/org/labs/App.java b/src/main/java/org/labs/App.java index d41e9e0..29d33db 100644 --- a/src/main/java/org/labs/App.java +++ b/src/main/java/org/labs/App.java @@ -1,16 +1,20 @@ package org.labs; import java.util.ArrayList; -import java.util.stream.Stream; + +import org.labs.waiters.Waiter; public class App { public static void main(String[] args) { final var waiter = new Waiter(); - final var spoons = Stream.generate(Spoon::new).limit(Constants.PROGRAMMERS_COUNT).toList(); + final var spoons = new ArrayList(); + for (Integer i = 0; i < Constants.PROGRAMMERS_COUNT; i++) { + spoons.add(new Spoon(i)); + } final var programmers = new ArrayList(); for (Integer i = 0; i < Constants.PROGRAMMERS_COUNT; i++) { - Integer leftSpoonIdx = i == Constants.PROGRAMMERS_COUNT - 1 ? 0 : i; + Integer leftSpoonIdx = i; Integer rightSpoonIdx = i == 0 ? Constants.PROGRAMMERS_COUNT - 1 : i - 1; programmers.add(new Programmer(i, waiter, spoons.get(leftSpoonIdx), spoons.get(rightSpoonIdx))); @@ -23,6 +27,7 @@ public static void main(String[] args) { try { p.join(); } catch (InterruptedException e) { + System.out.println("Программист " + p.getProgrammerId() + " умер"); } } } diff --git a/src/main/java/org/labs/Constants.java b/src/main/java/org/labs/Constants.java index a19ed9b..f62fcc9 100644 --- a/src/main/java/org/labs/Constants.java +++ b/src/main/java/org/labs/Constants.java @@ -1,8 +1,10 @@ package org.labs; public class Constants { - public static final Integer PORTION_SIZE = 10; public static final Integer BITE_SIZE = 10; + + public static final Integer MIN_PORTION_SIZE = BITE_SIZE; + public static final Integer MAX_PORTION_SIZE = 1_000; public static final Integer PORTIONS_COUNT = 1_000_000; public static final Integer PROGRAMMERS_COUNT = 7; diff --git a/src/main/java/org/labs/Programmer.java b/src/main/java/org/labs/Programmer.java index 758e9c5..9f00656 100644 --- a/src/main/java/org/labs/Programmer.java +++ b/src/main/java/org/labs/Programmer.java @@ -1,52 +1,82 @@ package org.labs; +import org.labs.loggers.ConsoleLogger; +import org.labs.loggers.Logger; +import org.labs.waiters.Bowl; +import org.labs.waiters.Waiter; + public class Programmer extends Thread { private final Integer id; + private final Logger logger; private final Integer biteSize; - private Integer eaten = 0; + private Integer portionsEaten = 0; private Bowl bowl = new Bowl(); private final Waiter waiter; - private final Spoon leftSpoon; - private final Spoon rightSpoon; + private final Spoon firstSpoon; + private final Spoon secondSpoon; Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon) { - this(id, waiter, leftSpoon, rightSpoon, Constants.BITE_SIZE); + this(id, waiter, leftSpoon, rightSpoon, Constants.BITE_SIZE, new ConsoleLogger()); } - Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon, Integer biteSize) { + Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon, Integer biteSize, Logger logger) { this.id = id; this.waiter = waiter; - this.leftSpoon = leftSpoon; - this.rightSpoon = rightSpoon; - this.biteSize = biteSize; + if (leftSpoon.getId() < rightSpoon.getId()) { + this.firstSpoon = leftSpoon; + this.secondSpoon = rightSpoon; + } else { + this.firstSpoon = rightSpoon; + this.secondSpoon = leftSpoon; + } + + this.biteSize = biteSize; + this.logger = logger; } @Override public void run() { + logger.log("Программист", id.toString(), "начал есть"); while (true) { if (!waiter.hasMoreFood()) { break; } - if (bowl.isEmpty()) { - try { - bowl = waiter.pickBowl(); - } catch (Exception e) { - break; - } + logger.log("Программист", id.toString(), "просит новую порцию"); + try { + bowl = waiter.pickBowl(); + } catch (Exception e) { + break; } - if (leftSpoon.lock.tryLock() && rightSpoon.lock.tryLock()) { - Integer bitten = bowl.eat(biteSize); - eaten += bitten; + logger.log("Программист", id.toString(), "ждет ложку", firstSpoon.getId().toString()); + firstSpoon.lock.lock(); + logger.log("Программист", id.toString(), "взял ложку", firstSpoon.getId().toString()); - leftSpoon.lock.unlock(); - rightSpoon.lock.unlock(); + logger.log("Программист", id.toString(), "ждет ложку", secondSpoon.getId().toString()); + secondSpoon.lock.lock(); + logger.log("Программист", id.toString(), "взял ложку", secondSpoon.getId().toString()); + + while (!bowl.isEmpty()) { + bowl.eat(biteSize); } + portionsEaten++; + logger.log("Программист", id.toString(), "доел порцию"); + + secondSpoon.lock.unlock(); + logger.log("Программист", id.toString(), "положил ложку", secondSpoon.getId().toString()); + + firstSpoon.lock.unlock(); + logger.log("Программист", id.toString(), "положил ложку", firstSpoon.getId().toString()); } + logger.log("Программист", id.toString(), "наелся"); + } + + public Integer getProgrammerId() { + return id; } } diff --git a/src/main/java/org/labs/Spoon.java b/src/main/java/org/labs/Spoon.java index 4ff4351..bd26b97 100644 --- a/src/main/java/org/labs/Spoon.java +++ b/src/main/java/org/labs/Spoon.java @@ -4,5 +4,15 @@ import java.util.concurrent.locks.ReentrantLock; public class Spoon { + private final Integer id; + public Lock lock = new ReentrantLock(); + + Spoon(Integer id) { + this.id = id; + } + + public Integer getId() { + return id; + } } diff --git a/src/main/java/org/labs/Waiter.java b/src/main/java/org/labs/Waiter.java deleted file mode 100644 index ba85592..0000000 --- a/src/main/java/org/labs/Waiter.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.labs; - -public class Waiter { - private Integer bowlsCount; - private Integer portionSize; - - Waiter() { - this(Constants.PORTIONS_COUNT); - } - - Waiter(Integer bowlsCount) { - this(bowlsCount, Constants.PORTION_SIZE); - } - - Waiter(Integer bowlsCount, Integer portionSize) { - this.bowlsCount = bowlsCount; - this.portionSize = portionSize; - } - - synchronized Boolean hasMoreFood() { - return bowlsCount > 0; - } - - synchronized public Bowl pickBowl() throws Exception { - if (bowlsCount == 0) { - throw new Exception("No more portions left"); - } - - bowlsCount--; - return new Bowl(portionSize); - } -} diff --git a/src/main/java/org/labs/loggers/ConsoleLogger.java b/src/main/java/org/labs/loggers/ConsoleLogger.java new file mode 100644 index 0000000..647b2fa --- /dev/null +++ b/src/main/java/org/labs/loggers/ConsoleLogger.java @@ -0,0 +1,8 @@ +package org.labs.loggers; + +public class ConsoleLogger implements Logger { + @Override + public void log(String... messages) { + System.out.println(String.join(" ", messages)); + } +} diff --git a/src/main/java/org/labs/loggers/Logger.java b/src/main/java/org/labs/loggers/Logger.java new file mode 100644 index 0000000..51fe991 --- /dev/null +++ b/src/main/java/org/labs/loggers/Logger.java @@ -0,0 +1,5 @@ +package org.labs.loggers; + +public interface Logger { + void log(String... messages); +} diff --git a/src/main/java/org/labs/Bowl.java b/src/main/java/org/labs/waiters/Bowl.java similarity index 83% rename from src/main/java/org/labs/Bowl.java rename to src/main/java/org/labs/waiters/Bowl.java index b86730c..1770704 100644 --- a/src/main/java/org/labs/Bowl.java +++ b/src/main/java/org/labs/waiters/Bowl.java @@ -1,13 +1,13 @@ -package org.labs; +package org.labs.waiters; public class Bowl { private Integer foodUnits; - Bowl() { + public Bowl() { this(0); } - Bowl(Integer foodUnits) { + public Bowl(Integer foodUnits) { this.foodUnits = foodUnits; } diff --git a/src/main/java/org/labs/waiters/Waiter.java b/src/main/java/org/labs/waiters/Waiter.java new file mode 100644 index 0000000..9792e27 --- /dev/null +++ b/src/main/java/org/labs/waiters/Waiter.java @@ -0,0 +1,41 @@ +package org.labs.waiters; + +import java.util.Random; + +import org.labs.Constants; +import org.labs.loggers.ConsoleLogger; +import org.labs.loggers.Logger; + +public class Waiter { + private final Logger logger; + + private Integer bowlsCount; + private Random random = new Random(); + + public Waiter() { + this(Constants.PORTIONS_COUNT, new ConsoleLogger()); + } + + public Waiter(Integer bowlsCount, Logger logger) { + this.bowlsCount = bowlsCount; + this.logger = logger; + } + + synchronized public Boolean hasMoreFood() { + return bowlsCount > 0; + } + + synchronized public Bowl pickBowl() throws Exception { + if (bowlsCount == 0) { + throw new Exception("No more portions left"); + } + + bowlsCount--; + logger.log("Официант отдал миску. Мисок осталось", bowlsCount.toString()); + + Integer portionSize = random.nextInt(Constants.MAX_PORTION_SIZE - Constants.MIN_PORTION_SIZE + 1) + + Constants.MIN_PORTION_SIZE; + + return new Bowl(portionSize); + } +} From d2afff07be5cf112b757c134c2c7cb9edb69c308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=94=D0=B0=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=BE=D0=B2?= Date: Fri, 3 Oct 2025 15:28:12 +0300 Subject: [PATCH 04/10] =?UTF-8?q?=D0=9E=D1=82=D1=80=D0=B5=D1=84=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BB=20=D0=BB=D0=BE=D0=B3=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=B8=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=BD=D0=B5=D1=81=20=D0=BF=D0=BE=20=D0=BF=D0=B0?= =?UTF-8?q?=D0=BA=D0=B5=D1=82=D0=B0=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/labs/App.java | 11 ++- src/main/java/org/labs/Programmer.java | 82 ------------------- src/main/java/org/labs/Spoon.java | 18 ---- .../java/org/labs/loggers/ConsoleLogger.java | 8 +- .../java/org/labs/programmer/Programmer.java | 79 ++++++++++++++++++ .../org/labs/{waiters => resources}/Bowl.java | 2 +- src/main/java/org/labs/resources/Spoon.java | 33 ++++++++ .../labs/{waiters => resources}/Waiter.java | 10 +-- 8 files changed, 132 insertions(+), 111 deletions(-) delete mode 100644 src/main/java/org/labs/Programmer.java delete mode 100644 src/main/java/org/labs/Spoon.java create mode 100644 src/main/java/org/labs/programmer/Programmer.java rename src/main/java/org/labs/{waiters => resources}/Bowl.java (94%) create mode 100644 src/main/java/org/labs/resources/Spoon.java rename src/main/java/org/labs/{waiters => resources}/Waiter.java (73%) diff --git a/src/main/java/org/labs/App.java b/src/main/java/org/labs/App.java index 29d33db..f56d7c9 100644 --- a/src/main/java/org/labs/App.java +++ b/src/main/java/org/labs/App.java @@ -2,11 +2,14 @@ import java.util.ArrayList; -import org.labs.waiters.Waiter; +import org.labs.programmer.Programmer; +import org.labs.resources.Spoon; +import org.labs.resources.Waiter; public class App { public static void main(String[] args) { final var waiter = new Waiter(); + final var spoons = new ArrayList(); for (Integer i = 0; i < Constants.PROGRAMMERS_COUNT; i++) { spoons.add(new Spoon(i)); @@ -15,19 +18,19 @@ public static void main(String[] args) { final var programmers = new ArrayList(); for (Integer i = 0; i < Constants.PROGRAMMERS_COUNT; i++) { Integer leftSpoonIdx = i; - Integer rightSpoonIdx = i == 0 ? Constants.PROGRAMMERS_COUNT - 1 : i - 1; + Integer rightSpoonIdx = (i + 1) % spoons.size(); programmers.add(new Programmer(i, waiter, spoons.get(leftSpoonIdx), spoons.get(rightSpoonIdx))); } - for (var p : programmers) + for (var p : programmers) { p.start(); + } for (var p : programmers) { try { p.join(); } catch (InterruptedException e) { - System.out.println("Программист " + p.getProgrammerId() + " умер"); } } } diff --git a/src/main/java/org/labs/Programmer.java b/src/main/java/org/labs/Programmer.java deleted file mode 100644 index 9f00656..0000000 --- a/src/main/java/org/labs/Programmer.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.labs; - -import org.labs.loggers.ConsoleLogger; -import org.labs.loggers.Logger; -import org.labs.waiters.Bowl; -import org.labs.waiters.Waiter; - -public class Programmer extends Thread { - private final Integer id; - private final Logger logger; - - private final Integer biteSize; - - private Integer portionsEaten = 0; - private Bowl bowl = new Bowl(); - - private final Waiter waiter; - private final Spoon firstSpoon; - private final Spoon secondSpoon; - - Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon) { - this(id, waiter, leftSpoon, rightSpoon, Constants.BITE_SIZE, new ConsoleLogger()); - } - - Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon, Integer biteSize, Logger logger) { - this.id = id; - this.waiter = waiter; - - if (leftSpoon.getId() < rightSpoon.getId()) { - this.firstSpoon = leftSpoon; - this.secondSpoon = rightSpoon; - } else { - this.firstSpoon = rightSpoon; - this.secondSpoon = leftSpoon; - } - - this.biteSize = biteSize; - this.logger = logger; - } - - @Override - public void run() { - logger.log("Программист", id.toString(), "начал есть"); - while (true) { - if (!waiter.hasMoreFood()) { - break; - } - - logger.log("Программист", id.toString(), "просит новую порцию"); - try { - bowl = waiter.pickBowl(); - } catch (Exception e) { - break; - } - - logger.log("Программист", id.toString(), "ждет ложку", firstSpoon.getId().toString()); - firstSpoon.lock.lock(); - logger.log("Программист", id.toString(), "взял ложку", firstSpoon.getId().toString()); - - logger.log("Программист", id.toString(), "ждет ложку", secondSpoon.getId().toString()); - secondSpoon.lock.lock(); - logger.log("Программист", id.toString(), "взял ложку", secondSpoon.getId().toString()); - - while (!bowl.isEmpty()) { - bowl.eat(biteSize); - } - portionsEaten++; - logger.log("Программист", id.toString(), "доел порцию"); - - secondSpoon.lock.unlock(); - logger.log("Программист", id.toString(), "положил ложку", secondSpoon.getId().toString()); - - firstSpoon.lock.unlock(); - logger.log("Программист", id.toString(), "положил ложку", firstSpoon.getId().toString()); - } - logger.log("Программист", id.toString(), "наелся"); - } - - public Integer getProgrammerId() { - return id; - } -} diff --git a/src/main/java/org/labs/Spoon.java b/src/main/java/org/labs/Spoon.java deleted file mode 100644 index bd26b97..0000000 --- a/src/main/java/org/labs/Spoon.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.labs; - -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -public class Spoon { - private final Integer id; - - public Lock lock = new ReentrantLock(); - - Spoon(Integer id) { - this.id = id; - } - - public Integer getId() { - return id; - } -} diff --git a/src/main/java/org/labs/loggers/ConsoleLogger.java b/src/main/java/org/labs/loggers/ConsoleLogger.java index 647b2fa..9ae57f4 100644 --- a/src/main/java/org/labs/loggers/ConsoleLogger.java +++ b/src/main/java/org/labs/loggers/ConsoleLogger.java @@ -1,8 +1,14 @@ package org.labs.loggers; public class ConsoleLogger implements Logger { + private String name; + + public ConsoleLogger(String name) { + this.name = name; + } + @Override public void log(String... messages) { - System.out.println(String.join(" ", messages)); + System.out.println(name + " " + String.join(" ", messages)); } } diff --git a/src/main/java/org/labs/programmer/Programmer.java b/src/main/java/org/labs/programmer/Programmer.java new file mode 100644 index 0000000..7344cf2 --- /dev/null +++ b/src/main/java/org/labs/programmer/Programmer.java @@ -0,0 +1,79 @@ +package org.labs.programmer; + +import org.labs.Constants; +import org.labs.loggers.ConsoleLogger; +import org.labs.loggers.Logger; +import org.labs.resources.Bowl; +import org.labs.resources.Spoon; +import org.labs.resources.Waiter; + +public class Programmer extends Thread { + private final Integer id; + private final Logger logger; + + private final Integer biteSize; + + private Integer portionsEaten = 0; + private Bowl bowl = new Bowl(); + + private final Waiter waiter; + private final Spoon firstSpoon; + private final Spoon secondSpoon; + + public Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon) { + this(id, waiter, leftSpoon, rightSpoon, Constants.BITE_SIZE); + } + + public Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon, Integer biteSize) { + super("Программист " + id.toString()); + + this.id = id; + this.waiter = waiter; + + if (leftSpoon.getId() < rightSpoon.getId()) { + this.firstSpoon = leftSpoon; + this.secondSpoon = rightSpoon; + } else { + this.firstSpoon = rightSpoon; + this.secondSpoon = leftSpoon; + } + + this.biteSize = biteSize; + this.logger = new ConsoleLogger(getName()); + } + + @Override + public void run() { + logger.log("начал есть"); + while (true) { + if (!waiter.hasMoreFood()) { + break; + } + + logger.log("просит новую порцию"); + try { + bowl = waiter.pickBowl(); + } catch (Exception e) { + break; + } + + firstSpoon.take(getName()); + secondSpoon.take(getName()); + + while (!bowl.isEmpty()) { + bowl.eat(biteSize); + } + portionsEaten++; + logger.log("доел порцию"); + + secondSpoon.put(getName()); + firstSpoon.put(getName()); + } + + logger.log("съел", portionsEaten.toString()); + } + + public Integer getProgrammerId() { + return id; + } +} diff --git a/src/main/java/org/labs/waiters/Bowl.java b/src/main/java/org/labs/resources/Bowl.java similarity index 94% rename from src/main/java/org/labs/waiters/Bowl.java rename to src/main/java/org/labs/resources/Bowl.java index 1770704..bf6ca28 100644 --- a/src/main/java/org/labs/waiters/Bowl.java +++ b/src/main/java/org/labs/resources/Bowl.java @@ -1,4 +1,4 @@ -package org.labs.waiters; +package org.labs.resources; public class Bowl { private Integer foodUnits; diff --git a/src/main/java/org/labs/resources/Spoon.java b/src/main/java/org/labs/resources/Spoon.java new file mode 100644 index 0000000..075a947 --- /dev/null +++ b/src/main/java/org/labs/resources/Spoon.java @@ -0,0 +1,33 @@ +package org.labs.resources; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.labs.loggers.ConsoleLogger; +import org.labs.loggers.Logger; + +public class Spoon { + private final Integer id; + private final Logger logger; + private Lock lock = new ReentrantLock(); + + public Spoon(Integer id) { + this.id = id; + this.logger = new ConsoleLogger("Ложка " + id.toString()); + } + + public void take(String actor) { + logger.log(actor, "пытается взять ложку"); + lock.lock(); + logger.log(actor, "взял ложку"); + } + + public void put(String actor) { + lock.unlock(); + logger.log(actor, "положил ложку"); + } + + public Integer getId() { + return id; + } +} diff --git a/src/main/java/org/labs/waiters/Waiter.java b/src/main/java/org/labs/resources/Waiter.java similarity index 73% rename from src/main/java/org/labs/waiters/Waiter.java rename to src/main/java/org/labs/resources/Waiter.java index 9792e27..e8d50e3 100644 --- a/src/main/java/org/labs/waiters/Waiter.java +++ b/src/main/java/org/labs/resources/Waiter.java @@ -1,4 +1,4 @@ -package org.labs.waiters; +package org.labs.resources; import java.util.Random; @@ -13,12 +13,12 @@ public class Waiter { private Random random = new Random(); public Waiter() { - this(Constants.PORTIONS_COUNT, new ConsoleLogger()); + this(Constants.PORTIONS_COUNT); } - public Waiter(Integer bowlsCount, Logger logger) { + public Waiter(Integer bowlsCount) { this.bowlsCount = bowlsCount; - this.logger = logger; + this.logger = new ConsoleLogger("Официант"); } synchronized public Boolean hasMoreFood() { @@ -31,7 +31,7 @@ synchronized public Bowl pickBowl() throws Exception { } bowlsCount--; - logger.log("Официант отдал миску. Мисок осталось", bowlsCount.toString()); + logger.log("отдал миску.", "Осталось", bowlsCount.toString()); Integer portionSize = random.nextInt(Constants.MAX_PORTION_SIZE - Constants.MIN_PORTION_SIZE + 1) + Constants.MIN_PORTION_SIZE; From e17d9364e5da7c2d1251784f0c3feb3200adbff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=94=D0=B0=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=BE=D0=B2?= Date: Sat, 4 Oct 2025 11:54:55 +0300 Subject: [PATCH 05/10] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D1=80=D0=B0=D0=BD=D0=B4=D0=BE=D0=BC=D0=BD=D1=8B=D0=B9?= =?UTF-8?q?=20sleep=20=D0=B2=20=D0=BF=D1=80=D0=BE=D0=B3=D1=80=D0=B0=D0=BC?= =?UTF-8?q?=D0=BC=D0=B8=D1=81=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/labs/Constants.java | 6 +- .../java/org/labs/programmer/Programmer.java | 61 +++++++++---------- src/main/java/org/labs/resources/Bowl.java | 27 -------- src/main/java/org/labs/resources/Waiter.java | 10 +-- 4 files changed, 30 insertions(+), 74 deletions(-) delete mode 100644 src/main/java/org/labs/resources/Bowl.java diff --git a/src/main/java/org/labs/Constants.java b/src/main/java/org/labs/Constants.java index f62fcc9..fd7d4e0 100644 --- a/src/main/java/org/labs/Constants.java +++ b/src/main/java/org/labs/Constants.java @@ -1,11 +1,7 @@ package org.labs; public class Constants { - public static final Integer BITE_SIZE = 10; - - public static final Integer MIN_PORTION_SIZE = BITE_SIZE; - public static final Integer MAX_PORTION_SIZE = 1_000; + public static final Integer MAX_EATING_TIME = 100; public static final Integer PORTIONS_COUNT = 1_000_000; - public static final Integer PROGRAMMERS_COUNT = 7; } diff --git a/src/main/java/org/labs/programmer/Programmer.java b/src/main/java/org/labs/programmer/Programmer.java index 7344cf2..0325fed 100644 --- a/src/main/java/org/labs/programmer/Programmer.java +++ b/src/main/java/org/labs/programmer/Programmer.java @@ -1,30 +1,25 @@ package org.labs.programmer; +import java.util.Random; + import org.labs.Constants; import org.labs.loggers.ConsoleLogger; import org.labs.loggers.Logger; -import org.labs.resources.Bowl; import org.labs.resources.Spoon; import org.labs.resources.Waiter; public class Programmer extends Thread { private final Integer id; private final Logger logger; - - private final Integer biteSize; + private final Random random = new Random(); private Integer portionsEaten = 0; - private Bowl bowl = new Bowl(); private final Waiter waiter; private final Spoon firstSpoon; private final Spoon secondSpoon; public Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon) { - this(id, waiter, leftSpoon, rightSpoon, Constants.BITE_SIZE); - } - - public Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon, Integer biteSize) { super("Программист " + id.toString()); this.id = id; @@ -38,39 +33,39 @@ public Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon, this.secondSpoon = leftSpoon; } - this.biteSize = biteSize; this.logger = new ConsoleLogger(getName()); } @Override public void run() { - logger.log("начал есть"); - while (true) { - if (!waiter.hasMoreFood()) { - break; - } - - logger.log("просит новую порцию"); - try { - bowl = waiter.pickBowl(); - } catch (Exception e) { - break; + try { + logger.log("начал есть"); + while (true) { + if (!waiter.hasMoreFood()) { + break; + } + + logger.log("просит новую порцию"); + try { + waiter.pickBowl(); + } catch (Exception e) { + break; + } + + firstSpoon.take(getName()); + secondSpoon.take(getName()); + + Thread.sleep(random.nextInt(Constants.MAX_EATING_TIME)); + logger.log("доел порцию"); + + secondSpoon.put(getName()); + firstSpoon.put(getName()); } - firstSpoon.take(getName()); - secondSpoon.take(getName()); - - while (!bowl.isEmpty()) { - bowl.eat(biteSize); - } - portionsEaten++; - logger.log("доел порцию"); - - secondSpoon.put(getName()); - firstSpoon.put(getName()); + logger.log("съел", portionsEaten.toString(), "порций"); + } catch (InterruptedException e) { + e.printStackTrace(); } - - logger.log("съел", portionsEaten.toString()); } public Integer getProgrammerId() { diff --git a/src/main/java/org/labs/resources/Bowl.java b/src/main/java/org/labs/resources/Bowl.java deleted file mode 100644 index bf6ca28..0000000 --- a/src/main/java/org/labs/resources/Bowl.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.labs.resources; - -public class Bowl { - private Integer foodUnits; - - public Bowl() { - this(0); - } - - public Bowl(Integer foodUnits) { - this.foodUnits = foodUnits; - } - - public Integer getFoodUnits() { - return foodUnits; - } - - public Integer eat(Integer bite) { - Integer bitten = foodUnits < bite ? foodUnits : bite; - foodUnits -= bitten; - return bitten; - } - - public Boolean isEmpty() { - return foodUnits == 0; - } -} diff --git a/src/main/java/org/labs/resources/Waiter.java b/src/main/java/org/labs/resources/Waiter.java index e8d50e3..b3b81ae 100644 --- a/src/main/java/org/labs/resources/Waiter.java +++ b/src/main/java/org/labs/resources/Waiter.java @@ -1,7 +1,5 @@ package org.labs.resources; -import java.util.Random; - import org.labs.Constants; import org.labs.loggers.ConsoleLogger; import org.labs.loggers.Logger; @@ -10,7 +8,6 @@ public class Waiter { private final Logger logger; private Integer bowlsCount; - private Random random = new Random(); public Waiter() { this(Constants.PORTIONS_COUNT); @@ -25,17 +22,12 @@ synchronized public Boolean hasMoreFood() { return bowlsCount > 0; } - synchronized public Bowl pickBowl() throws Exception { + synchronized public void pickBowl() throws Exception { if (bowlsCount == 0) { throw new Exception("No more portions left"); } bowlsCount--; logger.log("отдал миску.", "Осталось", bowlsCount.toString()); - - Integer portionSize = random.nextInt(Constants.MAX_PORTION_SIZE - Constants.MIN_PORTION_SIZE + 1) - + Constants.MIN_PORTION_SIZE; - - return new Bowl(portionSize); } } From 5055831c306170d4e80bdf1d1c8434862d56ecbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=94=D0=B0=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=BE=D0=B2?= Date: Sat, 4 Oct 2025 13:31:03 +0300 Subject: [PATCH 06/10] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=20=D0=BE=D1=84?= =?UTF-8?q?=D0=B8=D1=86=D0=B8=D0=B0=D0=BD=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/labs/App.java | 23 ++++++-- src/main/java/org/labs/Constants.java | 5 +- .../{loggers => logger}/ConsoleLogger.java | 9 ++- .../org/labs/{loggers => logger}/Logger.java | 2 +- .../java/org/labs/programmer/Programmer.java | 34 +++++------ src/main/java/org/labs/resources/Waiter.java | 33 ----------- .../org/labs/{resources => spoon}/Spoon.java | 6 +- src/main/java/org/labs/waiter/Waiter.java | 35 ++++++++++++ .../java/org/labs/waiter/WaiterService.java | 56 +++++++++++++++++++ 9 files changed, 143 insertions(+), 60 deletions(-) rename src/main/java/org/labs/{loggers => logger}/ConsoleLogger.java (50%) rename src/main/java/org/labs/{loggers => logger}/Logger.java (70%) delete mode 100644 src/main/java/org/labs/resources/Waiter.java rename src/main/java/org/labs/{resources => spoon}/Spoon.java (87%) create mode 100644 src/main/java/org/labs/waiter/Waiter.java create mode 100644 src/main/java/org/labs/waiter/WaiterService.java diff --git a/src/main/java/org/labs/App.java b/src/main/java/org/labs/App.java index f56d7c9..6c6db53 100644 --- a/src/main/java/org/labs/App.java +++ b/src/main/java/org/labs/App.java @@ -2,13 +2,17 @@ import java.util.ArrayList; +import org.labs.logger.ConsoleLogger; +import org.labs.logger.Logger; import org.labs.programmer.Programmer; -import org.labs.resources.Spoon; -import org.labs.resources.Waiter; +import org.labs.spoon.Spoon; +import org.labs.waiter.WaiterService; public class App { public static void main(String[] args) { - final var waiter = new Waiter(); + final Logger logger = new ConsoleLogger(); + + final var waiterService = new WaiterService(); final var spoons = new ArrayList(); for (Integer i = 0; i < Constants.PROGRAMMERS_COUNT; i++) { @@ -20,7 +24,7 @@ public static void main(String[] args) { Integer leftSpoonIdx = i; Integer rightSpoonIdx = (i + 1) % spoons.size(); - programmers.add(new Programmer(i, waiter, spoons.get(leftSpoonIdx), spoons.get(rightSpoonIdx))); + programmers.add(new Programmer(i, waiterService, spoons.get(leftSpoonIdx), spoons.get(rightSpoonIdx))); } for (var p : programmers) { @@ -33,5 +37,16 @@ public static void main(String[] args) { } catch (InterruptedException e) { } } + + for (final var programmer : programmers) { + logger.log("Программист", programmer.getProgrammerId().toString(), "съел", + programmer.getEatenPortions().toString(), + "порций"); + } + + var servedPortions = waiterService.getServedPortions(); + for (final var portion : servedPortions.entrySet()) { + logger.log("Официант", portion.getKey().toString(), "разнес", portion.getValue().toString(), "порций"); + } } } \ No newline at end of file diff --git a/src/main/java/org/labs/Constants.java b/src/main/java/org/labs/Constants.java index fd7d4e0..239257a 100644 --- a/src/main/java/org/labs/Constants.java +++ b/src/main/java/org/labs/Constants.java @@ -1,7 +1,10 @@ package org.labs; public class Constants { - public static final Integer MAX_EATING_TIME = 100; + public static final Integer MAX_EATING_TIME_MS = 100; + public static final Integer MAX_SERVER_TIME_MS = 100; + public static final Integer PORTIONS_COUNT = 1_000_000; public static final Integer PROGRAMMERS_COUNT = 7; + public static final Integer WAITERS_COUNT = 2; } diff --git a/src/main/java/org/labs/loggers/ConsoleLogger.java b/src/main/java/org/labs/logger/ConsoleLogger.java similarity index 50% rename from src/main/java/org/labs/loggers/ConsoleLogger.java rename to src/main/java/org/labs/logger/ConsoleLogger.java index 9ae57f4..0c88bc5 100644 --- a/src/main/java/org/labs/loggers/ConsoleLogger.java +++ b/src/main/java/org/labs/logger/ConsoleLogger.java @@ -1,14 +1,19 @@ -package org.labs.loggers; +package org.labs.logger; public class ConsoleLogger implements Logger { private String name; + public ConsoleLogger() { + this(""); + } + public ConsoleLogger(String name) { this.name = name; } @Override public void log(String... messages) { - System.out.println(name + " " + String.join(" ", messages)); + final var prefix = !name.isEmpty() ? name + " " : ""; + System.out.println(prefix + String.join(" ", messages)); } } diff --git a/src/main/java/org/labs/loggers/Logger.java b/src/main/java/org/labs/logger/Logger.java similarity index 70% rename from src/main/java/org/labs/loggers/Logger.java rename to src/main/java/org/labs/logger/Logger.java index 51fe991..9802445 100644 --- a/src/main/java/org/labs/loggers/Logger.java +++ b/src/main/java/org/labs/logger/Logger.java @@ -1,4 +1,4 @@ -package org.labs.loggers; +package org.labs.logger; public interface Logger { void log(String... messages); diff --git a/src/main/java/org/labs/programmer/Programmer.java b/src/main/java/org/labs/programmer/Programmer.java index 0325fed..7a5089f 100644 --- a/src/main/java/org/labs/programmer/Programmer.java +++ b/src/main/java/org/labs/programmer/Programmer.java @@ -3,27 +3,27 @@ import java.util.Random; import org.labs.Constants; -import org.labs.loggers.ConsoleLogger; -import org.labs.loggers.Logger; -import org.labs.resources.Spoon; -import org.labs.resources.Waiter; +import org.labs.logger.ConsoleLogger; +import org.labs.logger.Logger; +import org.labs.spoon.Spoon; +import org.labs.waiter.WaiterService; public class Programmer extends Thread { private final Integer id; private final Logger logger; private final Random random = new Random(); - private Integer portionsEaten = 0; + private Integer eatenPortions = 0; - private final Waiter waiter; + private final WaiterService waiters; private final Spoon firstSpoon; private final Spoon secondSpoon; - public Programmer(Integer id, Waiter waiter, Spoon leftSpoon, Spoon rightSpoon) { - super("Программист " + id.toString()); + public Programmer(Integer id, WaiterService waiters, Spoon leftSpoon, Spoon rightSpoon) { + super("Программист " + id); this.id = id; - this.waiter = waiter; + this.waiters = waiters; if (leftSpoon.getId() < rightSpoon.getId()) { this.firstSpoon = leftSpoon; @@ -41,13 +41,12 @@ public void run() { try { logger.log("начал есть"); while (true) { - if (!waiter.hasMoreFood()) { - break; - } logger.log("просит новую порцию"); try { - waiter.pickBowl(); + if (!waiters.getPortion(getName())) { + break; + } } catch (Exception e) { break; } @@ -55,14 +54,13 @@ public void run() { firstSpoon.take(getName()); secondSpoon.take(getName()); - Thread.sleep(random.nextInt(Constants.MAX_EATING_TIME)); + Thread.sleep(random.nextInt(Constants.MAX_EATING_TIME_MS)); + eatenPortions++; logger.log("доел порцию"); secondSpoon.put(getName()); firstSpoon.put(getName()); } - - logger.log("съел", portionsEaten.toString(), "порций"); } catch (InterruptedException e) { e.printStackTrace(); } @@ -71,4 +69,8 @@ public void run() { public Integer getProgrammerId() { return id; } + + public Integer getEatenPortions() { + return eatenPortions; + } } diff --git a/src/main/java/org/labs/resources/Waiter.java b/src/main/java/org/labs/resources/Waiter.java deleted file mode 100644 index b3b81ae..0000000 --- a/src/main/java/org/labs/resources/Waiter.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.labs.resources; - -import org.labs.Constants; -import org.labs.loggers.ConsoleLogger; -import org.labs.loggers.Logger; - -public class Waiter { - private final Logger logger; - - private Integer bowlsCount; - - public Waiter() { - this(Constants.PORTIONS_COUNT); - } - - public Waiter(Integer bowlsCount) { - this.bowlsCount = bowlsCount; - this.logger = new ConsoleLogger("Официант"); - } - - synchronized public Boolean hasMoreFood() { - return bowlsCount > 0; - } - - synchronized public void pickBowl() throws Exception { - if (bowlsCount == 0) { - throw new Exception("No more portions left"); - } - - bowlsCount--; - logger.log("отдал миску.", "Осталось", bowlsCount.toString()); - } -} diff --git a/src/main/java/org/labs/resources/Spoon.java b/src/main/java/org/labs/spoon/Spoon.java similarity index 87% rename from src/main/java/org/labs/resources/Spoon.java rename to src/main/java/org/labs/spoon/Spoon.java index 075a947..81de942 100644 --- a/src/main/java/org/labs/resources/Spoon.java +++ b/src/main/java/org/labs/spoon/Spoon.java @@ -1,10 +1,10 @@ -package org.labs.resources; +package org.labs.spoon; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import org.labs.loggers.ConsoleLogger; -import org.labs.loggers.Logger; +import org.labs.logger.ConsoleLogger; +import org.labs.logger.Logger; public class Spoon { private final Integer id; diff --git a/src/main/java/org/labs/waiter/Waiter.java b/src/main/java/org/labs/waiter/Waiter.java new file mode 100644 index 0000000..213c310 --- /dev/null +++ b/src/main/java/org/labs/waiter/Waiter.java @@ -0,0 +1,35 @@ +package org.labs.waiter; + +import java.util.Random; + +import org.labs.Constants; +import org.labs.logger.ConsoleLogger; +import org.labs.logger.Logger; + +class Waiter { + private final Integer id; + + private final Logger logger; + private final Random random = new Random(); + + private Integer serverPortions = 0; + + public Waiter(Integer id) { + this.id = id; + this.logger = new ConsoleLogger("Официант " + id); + } + + public synchronized void serverPortion(String client) throws Exception { + logger.log("понес порцию", client); + Thread.sleep(random.nextInt(Constants.MAX_SERVER_TIME_MS)); + serverPortions++; + } + + public Integer getId() { + return id; + } + + public Integer getServedPortions() { + return serverPortions; + } +} diff --git a/src/main/java/org/labs/waiter/WaiterService.java b/src/main/java/org/labs/waiter/WaiterService.java new file mode 100644 index 0000000..7a4653d --- /dev/null +++ b/src/main/java/org/labs/waiter/WaiterService.java @@ -0,0 +1,56 @@ +package org.labs.waiter; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import org.labs.Constants; +import org.labs.logger.ConsoleLogger; +import org.labs.logger.Logger; + +public class WaiterService { + private final Logger logger; + + private final AtomicInteger portionsCount; + + private final List waiters; + private final AtomicInteger nextWaiter; + + public WaiterService() { + this(Constants.WAITERS_COUNT, Constants.PORTIONS_COUNT); + } + + public WaiterService(Integer waitersCount, Integer portionsCount) { + Waiter[] mutableWaiters = new Waiter[waitersCount]; + for (Integer i = 0; i < waitersCount; i++) { + mutableWaiters[i] = new Waiter(i); + } + waiters = List.of(mutableWaiters); + nextWaiter = new AtomicInteger(0); + this.portionsCount = new AtomicInteger(portionsCount); + logger = new ConsoleLogger("Сервис официантов"); + } + + public Boolean getPortion(String client) throws Exception { + logger.log(client, "просит порцию"); + + var idx = nextWaiter.getAndUpdate((val) -> (val + 1) % waiters.size()); + var waiter = waiters.get(idx); + + Integer remainingPortions = portionsCount.decrementAndGet(); + if (remainingPortions < 0) { + logger.log(client, "узнал, что еда закончилась"); + return false; + } + + waiter.serverPortion(client); + logger.log(client, "получил порцию"); + logger.log("порций осталось", remainingPortions.toString()); + return true; + } + + public Map getServedPortions() { + return waiters.stream().collect(Collectors.toMap(Waiter::getId, Waiter::getServedPortions)); + } +} From 7cb436048531f31d0061f900515d11c30ae4752f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=94=D0=B0=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=BE=D0=B2?= Date: Sat, 4 Oct 2025 13:36:53 +0300 Subject: [PATCH 07/10] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D0=B0=D0=BB=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20=D0=BD?= =?UTF-8?q?=D0=B0=20StringBuilder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/labs/App.java | 12 ++++-------- src/main/java/org/labs/spoon/Spoon.java | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/labs/App.java b/src/main/java/org/labs/App.java index 6c6db53..bfed102 100644 --- a/src/main/java/org/labs/App.java +++ b/src/main/java/org/labs/App.java @@ -2,16 +2,12 @@ import java.util.ArrayList; -import org.labs.logger.ConsoleLogger; -import org.labs.logger.Logger; import org.labs.programmer.Programmer; import org.labs.spoon.Spoon; import org.labs.waiter.WaiterService; public class App { public static void main(String[] args) { - final Logger logger = new ConsoleLogger(); - final var waiterService = new WaiterService(); final var spoons = new ArrayList(); @@ -39,14 +35,14 @@ public static void main(String[] args) { } for (final var programmer : programmers) { - logger.log("Программист", programmer.getProgrammerId().toString(), "съел", - programmer.getEatenPortions().toString(), - "порций"); + System.out.println(new StringBuilder().append("Программист ").append(programmer.getProgrammerId()) + .append(" съел ").append(programmer.getEatenPortions()).append(" порций").toString()); } var servedPortions = waiterService.getServedPortions(); for (final var portion : servedPortions.entrySet()) { - logger.log("Официант", portion.getKey().toString(), "разнес", portion.getValue().toString(), "порций"); + System.out.println(new StringBuilder().append("Официант ").append(portion.getKey()) + .append(" разнес ").append(portion.getValue()).append(" порций").toString()); } } } \ No newline at end of file diff --git a/src/main/java/org/labs/spoon/Spoon.java b/src/main/java/org/labs/spoon/Spoon.java index 81de942..a788c64 100644 --- a/src/main/java/org/labs/spoon/Spoon.java +++ b/src/main/java/org/labs/spoon/Spoon.java @@ -13,7 +13,7 @@ public class Spoon { public Spoon(Integer id) { this.id = id; - this.logger = new ConsoleLogger("Ложка " + id.toString()); + this.logger = new ConsoleLogger("Ложка " + id); } public void take(String actor) { From 9edef77fda4038248afc662249166f4607c49483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=94=D0=B0=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=BE=D0=B2?= Date: Sat, 4 Oct 2025 14:29:30 +0300 Subject: [PATCH 08/10] =?UTF-8?q?=D0=92=D0=BD=D0=B5=D1=81=20=D0=B2=D1=81?= =?UTF-8?q?=D1=8E=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D1=83=20=D0=B2=20=D0=BE?= =?UTF-8?q?=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BA=D0=BB?= =?UTF-8?q?=D0=B0=D1=81=D1=81=20Restaurant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/labs/App.java | 44 +------------- .../java/org/labs/restaurant/Restaurant.java | 59 +++++++++++++++++++ .../java/org/labs/waiter/WaiterService.java | 5 -- 3 files changed, 62 insertions(+), 46 deletions(-) create mode 100644 src/main/java/org/labs/restaurant/Restaurant.java diff --git a/src/main/java/org/labs/App.java b/src/main/java/org/labs/App.java index bfed102..6442975 100644 --- a/src/main/java/org/labs/App.java +++ b/src/main/java/org/labs/App.java @@ -1,48 +1,10 @@ package org.labs; -import java.util.ArrayList; - -import org.labs.programmer.Programmer; -import org.labs.spoon.Spoon; -import org.labs.waiter.WaiterService; +import org.labs.restaurant.Restaurant; public class App { public static void main(String[] args) { - final var waiterService = new WaiterService(); - - final var spoons = new ArrayList(); - for (Integer i = 0; i < Constants.PROGRAMMERS_COUNT; i++) { - spoons.add(new Spoon(i)); - } - - final var programmers = new ArrayList(); - for (Integer i = 0; i < Constants.PROGRAMMERS_COUNT; i++) { - Integer leftSpoonIdx = i; - Integer rightSpoonIdx = (i + 1) % spoons.size(); - - programmers.add(new Programmer(i, waiterService, spoons.get(leftSpoonIdx), spoons.get(rightSpoonIdx))); - } - - for (var p : programmers) { - p.start(); - } - - for (var p : programmers) { - try { - p.join(); - } catch (InterruptedException e) { - } - } - - for (final var programmer : programmers) { - System.out.println(new StringBuilder().append("Программист ").append(programmer.getProgrammerId()) - .append(" съел ").append(programmer.getEatenPortions()).append(" порций").toString()); - } - - var servedPortions = waiterService.getServedPortions(); - for (final var portion : servedPortions.entrySet()) { - System.out.println(new StringBuilder().append("Официант ").append(portion.getKey()) - .append(" разнес ").append(portion.getValue()).append(" порций").toString()); - } + var restaurant = new Restaurant(Constants.PROGRAMMERS_COUNT, Constants.WAITERS_COUNT, Constants.PORTIONS_COUNT); + restaurant.startDinner(); } } \ No newline at end of file diff --git a/src/main/java/org/labs/restaurant/Restaurant.java b/src/main/java/org/labs/restaurant/Restaurant.java new file mode 100644 index 0000000..dfd2e43 --- /dev/null +++ b/src/main/java/org/labs/restaurant/Restaurant.java @@ -0,0 +1,59 @@ +package org.labs.restaurant; + +import java.util.ArrayList; +import java.util.List; + +import org.labs.logger.ConsoleLogger; +import org.labs.logger.Logger; +import org.labs.programmer.Programmer; +import org.labs.spoon.Spoon; +import org.labs.waiter.WaiterService; + +public class Restaurant { + private final Logger logger; + + private final WaiterService waiters; + private final List spoons = new ArrayList<>(); + private final List programmers = new ArrayList<>(); + + public Restaurant(Integer programmresCount, Integer waitersCount, Integer portionsCount) { + logger = new ConsoleLogger("Ресторан"); + + waiters = new WaiterService(waitersCount, portionsCount); + + for (Integer i = 0; i < programmresCount; i++) { + spoons.add(new Spoon(i)); + } + + for (Integer i = 0; i < programmresCount; i++) { + Integer leftSpoonIdx = i; + Integer rightSpoonIdx = (i + 1) % spoons.size(); + + programmers.add(new Programmer(i, waiters, spoons.get(leftSpoonIdx), spoons.get(rightSpoonIdx))); + } + } + + public void startDinner() { + for (var p : programmers) { + p.start(); + } + + for (var p : programmers) { + try { + p.join(); + } catch (InterruptedException e) { + } + } + + for (final var programmer : programmers) { + logger.log(new StringBuilder().append("Программист ").append(programmer.getProgrammerId()) + .append(" съел ").append(programmer.getEatenPortions()).append(" порций").toString()); + } + + var servedPortions = waiters.getServedPortions(); + for (final var portion : servedPortions.entrySet()) { + logger.log(new StringBuilder().append("Официант ").append(portion.getKey()) + .append(" разнес ").append(portion.getValue()).append(" порций").toString()); + } + } +} diff --git a/src/main/java/org/labs/waiter/WaiterService.java b/src/main/java/org/labs/waiter/WaiterService.java index 7a4653d..9619699 100644 --- a/src/main/java/org/labs/waiter/WaiterService.java +++ b/src/main/java/org/labs/waiter/WaiterService.java @@ -5,7 +5,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import org.labs.Constants; import org.labs.logger.ConsoleLogger; import org.labs.logger.Logger; @@ -17,10 +16,6 @@ public class WaiterService { private final List waiters; private final AtomicInteger nextWaiter; - public WaiterService() { - this(Constants.WAITERS_COUNT, Constants.PORTIONS_COUNT); - } - public WaiterService(Integer waitersCount, Integer portionsCount) { Waiter[] mutableWaiters = new Waiter[waitersCount]; for (Integer i = 0; i < waitersCount; i++) { From eee4c38de022693fdbb4e3e901cd4bc244e30c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=94=D0=B0=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=BE=D0=B2?= Date: Sat, 4 Oct 2025 15:04:36 +0300 Subject: [PATCH 09/10] =?UTF-8?q?=D0=92=D1=8B=D0=BD=D0=B5=D1=81=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=BD?= =?UTF-8?q?=D1=84=D1=8B=20=D0=BE=D0=B1=20=D1=83=D0=B6=D0=B8=D0=BD=D0=B5=20?= =?UTF-8?q?=D0=B2=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9?= =?UTF-8?q?=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/labs/App.java | 11 ++++++++++ .../java/org/labs/restaurant/DinnerInfo.java | 6 ++++++ .../java/org/labs/restaurant/Restaurant.java | 20 ++++++++++--------- 3 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/labs/restaurant/DinnerInfo.java diff --git a/src/main/java/org/labs/App.java b/src/main/java/org/labs/App.java index 6442975..672e514 100644 --- a/src/main/java/org/labs/App.java +++ b/src/main/java/org/labs/App.java @@ -6,5 +6,16 @@ public class App { public static void main(String[] args) { var restaurant = new Restaurant(Constants.PROGRAMMERS_COUNT, Constants.WAITERS_COUNT, Constants.PORTIONS_COUNT); restaurant.startDinner(); + var dinnerInfo = restaurant.getDinnerInfo(); + + for (var programmerInfo : dinnerInfo.programmersEatenPortions().entrySet()) { + System.out.println(new StringBuilder().append("Программист ").append(programmerInfo.getKey()) + .append(" съел ").append(programmerInfo.getValue()).append(" порций").toString()); + } + + for (var portion : dinnerInfo.waitersServedPortions().entrySet()) { + System.out.println(new StringBuilder().append("Официант ").append(portion.getKey()) + .append(" разнес ").append(portion.getValue()).append(" порций").toString()); + } } } \ No newline at end of file diff --git a/src/main/java/org/labs/restaurant/DinnerInfo.java b/src/main/java/org/labs/restaurant/DinnerInfo.java new file mode 100644 index 0000000..adc9ff4 --- /dev/null +++ b/src/main/java/org/labs/restaurant/DinnerInfo.java @@ -0,0 +1,6 @@ +package org.labs.restaurant; + +import java.util.Map; + +public record DinnerInfo(Map programmersEatenPortions, Map waitersServedPortions) { +} diff --git a/src/main/java/org/labs/restaurant/Restaurant.java b/src/main/java/org/labs/restaurant/Restaurant.java index dfd2e43..3f17116 100644 --- a/src/main/java/org/labs/restaurant/Restaurant.java +++ b/src/main/java/org/labs/restaurant/Restaurant.java @@ -2,6 +2,8 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import org.labs.logger.ConsoleLogger; import org.labs.logger.Logger; @@ -38,6 +40,8 @@ public void startDinner() { p.start(); } + logger.log("начал ужин"); + for (var p : programmers) { try { p.join(); @@ -45,15 +49,13 @@ public void startDinner() { } } - for (final var programmer : programmers) { - logger.log(new StringBuilder().append("Программист ").append(programmer.getProgrammerId()) - .append(" съел ").append(programmer.getEatenPortions()).append(" порций").toString()); - } + logger.log("закончил ужин"); + } - var servedPortions = waiters.getServedPortions(); - for (final var portion : servedPortions.entrySet()) { - logger.log(new StringBuilder().append("Официант ").append(portion.getKey()) - .append(" разнес ").append(portion.getValue()).append(" порций").toString()); - } + public DinnerInfo getDinnerInfo() { + Map programmersEatenPortions = programmers.stream() + .collect(Collectors.toMap(Programmer::getProgrammerId, Programmer::getEatenPortions)); + Map waitersServedPortions = waiters.getServedPortions(); + return new DinnerInfo(programmersEatenPortions, waitersServedPortions); } } From 9df1c0b898375ab82bcc295471e28d27df51bfb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=94=D0=B0=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=BE=D0=B2?= Date: Sat, 4 Oct 2025 15:55:52 +0300 Subject: [PATCH 10/10] =?UTF-8?q?=D0=9F=D0=BE=D0=BA=D1=80=D1=8B=D0=BB=20?= =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=D0=B0=D0=BC=D0=B8=20Restaurant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/org/labs/TestConstants.java | 13 ++ .../org/labs/restaurant/RestaurantTest.java | 113 ++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/test/java/org/labs/TestConstants.java create mode 100644 src/test/java/org/labs/restaurant/RestaurantTest.java diff --git a/src/test/java/org/labs/TestConstants.java b/src/test/java/org/labs/TestConstants.java new file mode 100644 index 0000000..1e03253 --- /dev/null +++ b/src/test/java/org/labs/TestConstants.java @@ -0,0 +1,13 @@ +package org.labs; + +public class TestConstants { + public static final Integer MAX_EATING_TIME_MS = 1; + public static final Integer MAX_SERVER_TIME_MS = 1; + + public static final Integer PORTIONS_COUNT = 100; + public static final Integer PROGRAMMERS_COUNT = 7; + public static final Integer WAITERS_COUNT = 2; + + public static final Float AVERAGE_DELTA_PERCENT = 0.1f; + public static final Float DIFF_PERCENT = 0.25f; +} diff --git a/src/test/java/org/labs/restaurant/RestaurantTest.java b/src/test/java/org/labs/restaurant/RestaurantTest.java new file mode 100644 index 0000000..39dced2 --- /dev/null +++ b/src/test/java/org/labs/restaurant/RestaurantTest.java @@ -0,0 +1,113 @@ +package org.labs.restaurant; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map.Entry; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.RepeatedTest; +import org.labs.TestConstants; + +public class RestaurantTest { + private Restaurant restaurant; + private DinnerInfo dinnerInfo; + + @BeforeEach + void setup() { + restaurant = new Restaurant(TestConstants.PROGRAMMERS_COUNT, TestConstants.WAITERS_COUNT, + TestConstants.PORTIONS_COUNT); + restaurant.startDinner(); + dinnerInfo = restaurant.getDinnerInfo(); + } + + @RepeatedTest(value = 5) + void getDinnerInfo_waitersServedAllPortions() { + var serverPortions = dinnerInfo.waitersServedPortions().entrySet().stream().map(Entry::getValue) + .reduce(0, Integer::sum); + + assertEquals(TestConstants.PORTIONS_COUNT, serverPortions, "Официанты разнесли все порции"); + } + + @RepeatedTest(value = 5) + void getDinnerInfo_waitersServedPortionsAverageFairness() { + var expectedAverage = TestConstants.PORTIONS_COUNT.floatValue() / TestConstants.WAITERS_COUNT; + var actualAverage = dinnerInfo.waitersServedPortions().entrySet().stream().mapToInt(Entry::getValue) + .average(); + + assertTrue(actualAverage.isPresent()); + assertEquals(expectedAverage, actualAverage.getAsDouble(), + TestConstants.AVERAGE_DELTA_PERCENT * expectedAverage, + "Среднее кол-во разнесенных официантом порций не отклоняется от ожидаемого более чем на " + + TestConstants.AVERAGE_DELTA_PERCENT * 100 + "%"); + } + + @RepeatedTest(value = 5) + void getDinnerInfo_waitersServedPortionsMaxFairness() { + var expectedAverage = TestConstants.PORTIONS_COUNT / TestConstants.WAITERS_COUNT; + var actualMax = dinnerInfo.waitersServedPortions().entrySet().stream().mapToInt(Entry::getValue) + .max(); + + assertTrue(actualMax.isPresent()); + assertTrue(Math.abs(expectedAverage - actualMax.getAsInt()) < TestConstants.DIFF_PERCENT * expectedAverage, + "Максимальное кол-во разнесенных официантом порций не отклоняется от ожидаемого более чем на " + + TestConstants.DIFF_PERCENT * 100 + "%"); + } + + @RepeatedTest(value = 5) + void getDinnerInfo_waitersServedPortionsMinFairness() { + var expectedAverage = TestConstants.PORTIONS_COUNT / TestConstants.WAITERS_COUNT; + var actualMin = dinnerInfo.waitersServedPortions().entrySet().stream().mapToInt(Entry::getValue) + .min(); + + assertTrue(actualMin.isPresent()); + assertTrue(Math.abs(expectedAverage - actualMin.getAsInt()) < TestConstants.DIFF_PERCENT * expectedAverage, + "Минимальное кол-во разнесенных официантом порций не отклоняется от ожидаемого более чем на " + + TestConstants.DIFF_PERCENT * 100 + "%"); + } + + @RepeatedTest(value = 5) + void getDinnerInfo_programmersEatenAllPortions() { + var eatenPortions = dinnerInfo.programmersEatenPortions().entrySet().stream().mapToInt(Entry::getValue) + .sum(); + + assertEquals(TestConstants.PORTIONS_COUNT, eatenPortions, "Программисты съели все порции"); + } + + @RepeatedTest(value = 5) + void getDinnerInfo_programmersEatenPortionAverageFairness() { + var expectedAverage = TestConstants.PORTIONS_COUNT.floatValue() / TestConstants.PROGRAMMERS_COUNT; + var actualAverage = dinnerInfo.programmersEatenPortions().entrySet().stream().mapToInt(Entry::getValue) + .average(); + + assertTrue(actualAverage.isPresent()); + assertEquals(expectedAverage, actualAverage.getAsDouble(), + TestConstants.AVERAGE_DELTA_PERCENT * expectedAverage, + "Среднее кол-во съеденных порций не отклоняется от ожидаемого более чем на " + + TestConstants.AVERAGE_DELTA_PERCENT * 100 + "%"); + } + + @RepeatedTest(value = 5) + void getDinnerInfo_programmersEatenPortionMaxFairness() { + var expectedAverage = TestConstants.PORTIONS_COUNT / TestConstants.PROGRAMMERS_COUNT; + var actualMax = dinnerInfo.programmersEatenPortions().entrySet().stream().mapToInt(Entry::getValue) + .max(); + + assertTrue(actualMax.isPresent()); + assertTrue(Math.abs(expectedAverage - actualMax.getAsInt()) < TestConstants.DIFF_PERCENT * expectedAverage, + "Максимальное кол-во съеденных программистом порций не отклоняется от ожидаемого более чем на " + + TestConstants.DIFF_PERCENT * 100 + "%"); + } + + @RepeatedTest(value = 5) + void getDinnerInfo_programmersEatenPortionMinFairness() { + var expectedAverage = TestConstants.PORTIONS_COUNT / TestConstants.PROGRAMMERS_COUNT; + var actualMin = dinnerInfo.programmersEatenPortions().entrySet().stream().mapToInt(Entry::getValue) + .min(); + + assertTrue(actualMin.isPresent()); + assertTrue(Math.abs(expectedAverage - actualMin.getAsInt()) < TestConstants.DIFF_PERCENT * expectedAverage, + "Минимальное кол-во съеденных программистом порций не отклоняется от ожидаемого более чем на " + + TestConstants.DIFF_PERCENT * 100 + "%"); + } +}