From 607b1a17d9d2aef0aac44fbd3c9c8562ec5809e9 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 12:49:42 +0000 Subject: [PATCH 01/11] 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 dafa9a00e2cabf12e2e58c0ceec89b5a2088792f Mon Sep 17 00:00:00 2001 From: jbisss Date: Tue, 23 Sep 2025 23:42:27 +0300 Subject: [PATCH 02/11] [feature] non-beautiful code --- src/main/java/org/labs/Main.java | 131 ++++++++++++++++++++++- src/main/java/org/labs/items/Spoon.java | 4 + src/main/java/org/labs/items/Waiter.java | 18 ++++ 3 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/labs/items/Spoon.java create mode 100644 src/main/java/org/labs/items/Waiter.java diff --git a/src/main/java/org/labs/Main.java b/src/main/java/org/labs/Main.java index 9917247..934a3f7 100644 --- a/src/main/java/org/labs/Main.java +++ b/src/main/java/org/labs/Main.java @@ -1,7 +1,134 @@ package org.labs; +import org.labs.items.Spoon; +import org.labs.items.Waiter; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; + public class Main { - public static void main(String[] args) { - System.out.println("Hello, World!"); + + private static AtomicInteger portionsAmount; + private static Waiter[] waiters; + private static Spoon[] spoons; + private static Programmer[] programmers; + + private static BlockingQueue waiterBlockingQueue; + + private static final Object lock = new Object(); + + static class Programmer extends Thread { + + private final Spoon leftSpoon; + private final Spoon rightSpoon; + + private int portionsConsumed = 0; + + Programmer(String programmerName, Spoon leftSpoon, Spoon rightSpoon) { + super(programmerName); + this.leftSpoon = leftSpoon; + this.rightSpoon = rightSpoon; + } + + private synchronized void printMessage(String message) throws InterruptedException { + int ms = (int) (Math.random() * 100); + System.out.println("Programmer_" + Thread.currentThread().getName() + " " + message + " for: " + ms + "ms"); + Thread.sleep(ms); + } + + public void trashTalk() throws InterruptedException { + printMessage("is trash-talking"); + } + + public boolean requestPortion() throws InterruptedException { + Waiter waiter = waiterBlockingQueue.take(); + boolean portionTaken = waiter.tryTakePortion(); + waiterBlockingQueue.put(waiter); + return portionTaken; + } + + public void takeSpoons() throws InterruptedException { + synchronized (leftSpoon) { + printMessage("takes left spoon"); + synchronized (rightSpoon) { + printMessage("takes right spoon"); + } + } + } + + public void eat() throws InterruptedException { + synchronized (lock) { + if (portionsAmount.get() > 0) { + printMessage("is eating"); + portionsAmount.getAndDecrement(); + this.portionsConsumed++; + } + } + } + + public void putSpoons() throws InterruptedException { + printMessage("releasing spoons"); + } + + @Override + public void run() { + try { + while (portionsAmount.get() > 1) { + trashTalk(); + boolean portionTaken = requestPortion(); + if (!portionTaken) continue; + takeSpoons(); + eat(); + putSpoons(); + } + System.out.println("Done " + this.getName() + " with consumed: " + this.portionsConsumed); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + private static final int DEFAULT_PORTIONS_AMOUNT = 200; + private static final int DEFAULT_WAITERS_AMOUNT = 2; + private static final int DEFAULT_PROGRAMMERS_AMOUNT = 10; + + public static void main(String[] args) throws InterruptedException { + Waiter.portionsAmount = DEFAULT_PORTIONS_AMOUNT; + + portionsAmount = new AtomicInteger(DEFAULT_PORTIONS_AMOUNT); + waiters = new Waiter[DEFAULT_WAITERS_AMOUNT]; + spoons = new Spoon[DEFAULT_PROGRAMMERS_AMOUNT]; + programmers = new Programmer[DEFAULT_PROGRAMMERS_AMOUNT]; + waiterBlockingQueue = new ArrayBlockingQueue<>(DEFAULT_WAITERS_AMOUNT); + + for (int i = 0; i < DEFAULT_WAITERS_AMOUNT; i++) { + waiters[i] = new Waiter(); + waiterBlockingQueue.put(waiters[i]); + } + for (int i = 0; i < DEFAULT_PROGRAMMERS_AMOUNT; i++) { + spoons[i] = new Spoon(); + } + + for (int i = 0; i < DEFAULT_PROGRAMMERS_AMOUNT; i++) { + Spoon leftSpoon = spoons[i]; + Spoon rightSpoon = spoons[(i + 1) % DEFAULT_PROGRAMMERS_AMOUNT]; + + Programmer currentProgrammer = i == DEFAULT_PROGRAMMERS_AMOUNT - 1 + ? new Programmer(String.valueOf(i), rightSpoon, leftSpoon) + : new Programmer(String.valueOf(i), leftSpoon, rightSpoon); + currentProgrammer.start(); + programmers[i] = currentProgrammer; + } + + for (int i = 0; i < DEFAULT_PROGRAMMERS_AMOUNT; i++) { + programmers[i].join(); + } + int sumPortionsFromProgrammer = 0; + for (int i = 0; i < DEFAULT_PROGRAMMERS_AMOUNT; i++) { + sumPortionsFromProgrammer += programmers[i].portionsConsumed; + } + System.out.println("Food left: " + portionsAmount); + System.out.println("Total consumed food: " + sumPortionsFromProgrammer); } } \ No newline at end of file diff --git a/src/main/java/org/labs/items/Spoon.java b/src/main/java/org/labs/items/Spoon.java new file mode 100644 index 0000000..f54a69b --- /dev/null +++ b/src/main/java/org/labs/items/Spoon.java @@ -0,0 +1,4 @@ +package org.labs.items; + +public class Spoon { +} diff --git a/src/main/java/org/labs/items/Waiter.java b/src/main/java/org/labs/items/Waiter.java new file mode 100644 index 0000000..ac634cf --- /dev/null +++ b/src/main/java/org/labs/items/Waiter.java @@ -0,0 +1,18 @@ +package org.labs.items; + +public class Waiter { + + public static int portionsAmount; + + private static final Object waitersLock = new Object(); + + public boolean tryTakePortion() { + synchronized (waitersLock) { + if (portionsAmount > 0) { + portionsAmount--; + return true; + } + return false; + } + } +} From d668330fb022f7d57deb041af78e56f9945f2c43 Mon Sep 17 00:00:00 2001 From: jbisss Date: Wed, 24 Sep 2025 00:41:17 +0300 Subject: [PATCH 03/11] [feature] some refactor --- src/main/java/org/labs/Main.java | 132 +----------------- .../labs/dining/DiningProgrammersWorld.java | 49 +++++++ .../java/org/labs/dining/SharedContext.java | 34 +++++ .../org/labs/dining/items/Programmer.java | 69 +++++++++ .../java/org/labs/dining/items/Spoon.java | 4 + .../org/labs/{ => dining}/items/Waiter.java | 2 +- src/main/java/org/labs/items/Spoon.java | 4 - 7 files changed, 164 insertions(+), 130 deletions(-) create mode 100644 src/main/java/org/labs/dining/DiningProgrammersWorld.java create mode 100644 src/main/java/org/labs/dining/SharedContext.java create mode 100644 src/main/java/org/labs/dining/items/Programmer.java create mode 100644 src/main/java/org/labs/dining/items/Spoon.java rename src/main/java/org/labs/{ => dining}/items/Waiter.java (92%) delete mode 100644 src/main/java/org/labs/items/Spoon.java diff --git a/src/main/java/org/labs/Main.java b/src/main/java/org/labs/Main.java index 934a3f7..98a18f8 100644 --- a/src/main/java/org/labs/Main.java +++ b/src/main/java/org/labs/Main.java @@ -1,134 +1,16 @@ package org.labs; -import org.labs.items.Spoon; -import org.labs.items.Waiter; +import org.labs.dining.DiningProgrammersWorld; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.Arrays; public class Main { - private static AtomicInteger portionsAmount; - private static Waiter[] waiters; - private static Spoon[] spoons; - private static Programmer[] programmers; - - private static BlockingQueue waiterBlockingQueue; - - private static final Object lock = new Object(); - - static class Programmer extends Thread { - - private final Spoon leftSpoon; - private final Spoon rightSpoon; - - private int portionsConsumed = 0; - - Programmer(String programmerName, Spoon leftSpoon, Spoon rightSpoon) { - super(programmerName); - this.leftSpoon = leftSpoon; - this.rightSpoon = rightSpoon; - } - - private synchronized void printMessage(String message) throws InterruptedException { - int ms = (int) (Math.random() * 100); - System.out.println("Programmer_" + Thread.currentThread().getName() + " " + message + " for: " + ms + "ms"); - Thread.sleep(ms); - } - - public void trashTalk() throws InterruptedException { - printMessage("is trash-talking"); - } - - public boolean requestPortion() throws InterruptedException { - Waiter waiter = waiterBlockingQueue.take(); - boolean portionTaken = waiter.tryTakePortion(); - waiterBlockingQueue.put(waiter); - return portionTaken; - } - - public void takeSpoons() throws InterruptedException { - synchronized (leftSpoon) { - printMessage("takes left spoon"); - synchronized (rightSpoon) { - printMessage("takes right spoon"); - } - } - } - - public void eat() throws InterruptedException { - synchronized (lock) { - if (portionsAmount.get() > 0) { - printMessage("is eating"); - portionsAmount.getAndDecrement(); - this.portionsConsumed++; - } - } - } - - public void putSpoons() throws InterruptedException { - printMessage("releasing spoons"); - } - - @Override - public void run() { - try { - while (portionsAmount.get() > 1) { - trashTalk(); - boolean portionTaken = requestPortion(); - if (!portionTaken) continue; - takeSpoons(); - eat(); - putSpoons(); - } - System.out.println("Done " + this.getName() + " with consumed: " + this.portionsConsumed); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - } - - private static final int DEFAULT_PORTIONS_AMOUNT = 200; - private static final int DEFAULT_WAITERS_AMOUNT = 2; - private static final int DEFAULT_PROGRAMMERS_AMOUNT = 10; - - public static void main(String[] args) throws InterruptedException { - Waiter.portionsAmount = DEFAULT_PORTIONS_AMOUNT; - - portionsAmount = new AtomicInteger(DEFAULT_PORTIONS_AMOUNT); - waiters = new Waiter[DEFAULT_WAITERS_AMOUNT]; - spoons = new Spoon[DEFAULT_PROGRAMMERS_AMOUNT]; - programmers = new Programmer[DEFAULT_PROGRAMMERS_AMOUNT]; - waiterBlockingQueue = new ArrayBlockingQueue<>(DEFAULT_WAITERS_AMOUNT); - - for (int i = 0; i < DEFAULT_WAITERS_AMOUNT; i++) { - waiters[i] = new Waiter(); - waiterBlockingQueue.put(waiters[i]); - } - for (int i = 0; i < DEFAULT_PROGRAMMERS_AMOUNT; i++) { - spoons[i] = new Spoon(); - } - - for (int i = 0; i < DEFAULT_PROGRAMMERS_AMOUNT; i++) { - Spoon leftSpoon = spoons[i]; - Spoon rightSpoon = spoons[(i + 1) % DEFAULT_PROGRAMMERS_AMOUNT]; - - Programmer currentProgrammer = i == DEFAULT_PROGRAMMERS_AMOUNT - 1 - ? new Programmer(String.valueOf(i), rightSpoon, leftSpoon) - : new Programmer(String.valueOf(i), leftSpoon, rightSpoon); - currentProgrammer.start(); - programmers[i] = currentProgrammer; - } - - for (int i = 0; i < DEFAULT_PROGRAMMERS_AMOUNT; i++) { - programmers[i].join(); - } - int sumPortionsFromProgrammer = 0; - for (int i = 0; i < DEFAULT_PROGRAMMERS_AMOUNT; i++) { - sumPortionsFromProgrammer += programmers[i].portionsConsumed; + public static void main(String[] args) { + try { + new DiningProgrammersWorld().startDining(1_000, 3, 7); + } catch (InterruptedException e) { + System.out.println("Ooopps... " + Arrays.toString(e.getStackTrace())); } - System.out.println("Food left: " + portionsAmount); - System.out.println("Total consumed food: " + sumPortionsFromProgrammer); } } \ No newline at end of file diff --git a/src/main/java/org/labs/dining/DiningProgrammersWorld.java b/src/main/java/org/labs/dining/DiningProgrammersWorld.java new file mode 100644 index 0000000..f362d15 --- /dev/null +++ b/src/main/java/org/labs/dining/DiningProgrammersWorld.java @@ -0,0 +1,49 @@ +package org.labs.dining; + +import org.labs.dining.items.Programmer; +import org.labs.dining.items.Spoon; +import org.labs.dining.items.Waiter; + +public class DiningProgrammersWorld { + + public static Programmer[] programmers; + + private static final int DEFAULT_PORTIONS_AMOUNT = 1_000_000; + private static final int DEFAULT_WAITERS_AMOUNT = 2; + private static final int DEFAULT_PROGRAMMERS_AMOUNT = 5; + + public void startDining( + Integer portionsAmount, + Integer waitersAmount, + Integer programmersAmount + ) throws InterruptedException { + int portionsAmountToInitialize = portionsAmount == null ? DEFAULT_PORTIONS_AMOUNT : portionsAmount; + int waitersAmountToInitialize = waitersAmount == null ? DEFAULT_WAITERS_AMOUNT : waitersAmount; + int programmersAmountToInitialize = programmersAmount == null ? DEFAULT_PROGRAMMERS_AMOUNT : programmersAmount; + + SharedContext.initialize(portionsAmountToInitialize, waitersAmountToInitialize, programmersAmountToInitialize); + programmers = new Programmer[programmersAmountToInitialize]; + + for (int i = 0; i < programmersAmountToInitialize; i++) { + Spoon leftSpoon = SharedContext.spoons[i]; + Spoon rightSpoon = SharedContext.spoons[(i + 1) % programmersAmountToInitialize]; + + Programmer currentProgrammer = i == programmersAmountToInitialize - 1 + ? new Programmer(String.valueOf(i), rightSpoon, leftSpoon) + : new Programmer(String.valueOf(i), leftSpoon, rightSpoon); + currentProgrammer.start(); + programmers[i] = currentProgrammer; + } + + for (int i = 0; i < programmersAmountToInitialize; i++) { + programmers[i].join(); + } + + int sumPortionsFromProgrammer = 0; + for (int i = 0; i < programmersAmountToInitialize; i++) { + sumPortionsFromProgrammer += programmers[i].portionsConsumed; + } + System.out.println("Food left: " + Waiter.portionsAmount); + System.out.println("Total consumed food: " + sumPortionsFromProgrammer); + } +} diff --git a/src/main/java/org/labs/dining/SharedContext.java b/src/main/java/org/labs/dining/SharedContext.java new file mode 100644 index 0000000..f0b8e38 --- /dev/null +++ b/src/main/java/org/labs/dining/SharedContext.java @@ -0,0 +1,34 @@ +package org.labs.dining; + +import org.labs.dining.items.Spoon; +import org.labs.dining.items.Waiter; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +public class SharedContext { + + public static Waiter[] waiters; + public static Spoon[] spoons; + public static BlockingQueue waiterBlockingQueue; + + public static void initialize( + int portionsAmount, + int waitersAmount, + int spoonsAmount + ) throws InterruptedException { + Waiter.portionsAmount = portionsAmount; + + SharedContext.waiters = new Waiter[waitersAmount]; + SharedContext.spoons = new Spoon[spoonsAmount]; + SharedContext.waiterBlockingQueue = new ArrayBlockingQueue<>(waitersAmount); + + for (int i = 0; i < waitersAmount; i++) { + SharedContext.waiters[i] = new Waiter(); + SharedContext.waiterBlockingQueue.put(SharedContext.waiters[i]); + } + for (int i = 0; i < spoonsAmount; i++) { + SharedContext.spoons[i] = new Spoon(); + } + } +} diff --git a/src/main/java/org/labs/dining/items/Programmer.java b/src/main/java/org/labs/dining/items/Programmer.java new file mode 100644 index 0000000..153d036 --- /dev/null +++ b/src/main/java/org/labs/dining/items/Programmer.java @@ -0,0 +1,69 @@ +package org.labs.dining.items; + +import org.labs.dining.SharedContext; + +public class Programmer extends Thread { + + private final Spoon leftSpoon; + private final Spoon rightSpoon; + + public int portionsConsumed = 0; + + public Programmer(String programmerName, Spoon leftSpoon, Spoon rightSpoon) { + super(programmerName); + this.leftSpoon = leftSpoon; + this.rightSpoon = rightSpoon; + } + + private synchronized void printMessage(String message) throws InterruptedException { + int ms = (int) (Math.random() * 10); + System.out.println("Programmer_" + Thread.currentThread().getName() + " " + message + " for: " + ms + "ms"); + Thread.sleep(ms); + } + + public void trashTalk() throws InterruptedException { + printMessage("is trash-talking"); + } + + public boolean requestPortion() throws InterruptedException { + Waiter waiter = SharedContext.waiterBlockingQueue.take(); + boolean portionTaken = waiter.tryTakePortion(); + SharedContext.waiterBlockingQueue.put(waiter); + return portionTaken; + } + + public void takeSpoons() throws InterruptedException { + synchronized (leftSpoon) { + printMessage("takes left spoon"); + synchronized (rightSpoon) { + printMessage("takes right spoon"); + } + } + } + + public void eat() throws InterruptedException { + printMessage("is eating"); + this.portionsConsumed++; + } + + public void putSpoons() throws InterruptedException { + printMessage("releasing spoons"); + } + + @Override + public void run() { + try { + while (true) { + trashTalk(); + boolean portionTaken = requestPortion(); + if (!portionTaken) break; + takeSpoons(); + eat(); + putSpoons(); + } + System.out.println("Done " + this.getName() + " with consumed: " + this.portionsConsumed); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/org/labs/dining/items/Spoon.java b/src/main/java/org/labs/dining/items/Spoon.java new file mode 100644 index 0000000..3a52719 --- /dev/null +++ b/src/main/java/org/labs/dining/items/Spoon.java @@ -0,0 +1,4 @@ +package org.labs.dining.items; + +public class Spoon { +} diff --git a/src/main/java/org/labs/items/Waiter.java b/src/main/java/org/labs/dining/items/Waiter.java similarity index 92% rename from src/main/java/org/labs/items/Waiter.java rename to src/main/java/org/labs/dining/items/Waiter.java index ac634cf..df37529 100644 --- a/src/main/java/org/labs/items/Waiter.java +++ b/src/main/java/org/labs/dining/items/Waiter.java @@ -1,4 +1,4 @@ -package org.labs.items; +package org.labs.dining.items; public class Waiter { diff --git a/src/main/java/org/labs/items/Spoon.java b/src/main/java/org/labs/items/Spoon.java deleted file mode 100644 index f54a69b..0000000 --- a/src/main/java/org/labs/items/Spoon.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.labs.items; - -public class Spoon { -} From 400c24ed005e4a759092a373fd6bbf4ae9465619 Mon Sep 17 00:00:00 2001 From: jbisss Date: Wed, 24 Sep 2025 00:43:11 +0300 Subject: [PATCH 04/11] [feature] programmer name --- src/main/java/org/labs/dining/DiningProgrammersWorld.java | 5 +++-- src/main/java/org/labs/dining/items/Programmer.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/labs/dining/DiningProgrammersWorld.java b/src/main/java/org/labs/dining/DiningProgrammersWorld.java index f362d15..e150b46 100644 --- a/src/main/java/org/labs/dining/DiningProgrammersWorld.java +++ b/src/main/java/org/labs/dining/DiningProgrammersWorld.java @@ -28,9 +28,10 @@ public void startDining( Spoon leftSpoon = SharedContext.spoons[i]; Spoon rightSpoon = SharedContext.spoons[(i + 1) % programmersAmountToInitialize]; + String programmerName = "Programmer_" + i; Programmer currentProgrammer = i == programmersAmountToInitialize - 1 - ? new Programmer(String.valueOf(i), rightSpoon, leftSpoon) - : new Programmer(String.valueOf(i), leftSpoon, rightSpoon); + ? new Programmer(programmerName, rightSpoon, leftSpoon) + : new Programmer(programmerName, leftSpoon, rightSpoon); currentProgrammer.start(); programmers[i] = currentProgrammer; } diff --git a/src/main/java/org/labs/dining/items/Programmer.java b/src/main/java/org/labs/dining/items/Programmer.java index 153d036..f13eb67 100644 --- a/src/main/java/org/labs/dining/items/Programmer.java +++ b/src/main/java/org/labs/dining/items/Programmer.java @@ -17,7 +17,7 @@ public Programmer(String programmerName, Spoon leftSpoon, Spoon rightSpoon) { private synchronized void printMessage(String message) throws InterruptedException { int ms = (int) (Math.random() * 10); - System.out.println("Programmer_" + Thread.currentThread().getName() + " " + message + " for: " + ms + "ms"); + System.out.println(Thread.currentThread().getName() + " " + message + " for: " + ms + "ms"); Thread.sleep(ms); } From d9410462864eb85444ef57ca81234ede831eb04c Mon Sep 17 00:00:00 2001 From: jbisss Date: Wed, 24 Sep 2025 00:45:39 +0300 Subject: [PATCH 05/11] [feature] removed inner 'test', test needed to be written --- src/main/java/org/labs/dining/DiningProgrammersWorld.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/org/labs/dining/DiningProgrammersWorld.java b/src/main/java/org/labs/dining/DiningProgrammersWorld.java index e150b46..53f37c6 100644 --- a/src/main/java/org/labs/dining/DiningProgrammersWorld.java +++ b/src/main/java/org/labs/dining/DiningProgrammersWorld.java @@ -2,7 +2,6 @@ import org.labs.dining.items.Programmer; import org.labs.dining.items.Spoon; -import org.labs.dining.items.Waiter; public class DiningProgrammersWorld { @@ -39,12 +38,5 @@ public void startDining( for (int i = 0; i < programmersAmountToInitialize; i++) { programmers[i].join(); } - - int sumPortionsFromProgrammer = 0; - for (int i = 0; i < programmersAmountToInitialize; i++) { - sumPortionsFromProgrammer += programmers[i].portionsConsumed; - } - System.out.println("Food left: " + Waiter.portionsAmount); - System.out.println("Total consumed food: " + sumPortionsFromProgrammer); } } From 693b6d40174ebdab8a61138f401be55ec8d2a395 Mon Sep 17 00:00:00 2001 From: jbisss Date: Wed, 24 Sep 2025 00:46:56 +0300 Subject: [PATCH 06/11] [feature] getter for programmer's consumed portions of food --- src/main/java/org/labs/dining/items/Programmer.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/labs/dining/items/Programmer.java b/src/main/java/org/labs/dining/items/Programmer.java index f13eb67..0c53869 100644 --- a/src/main/java/org/labs/dining/items/Programmer.java +++ b/src/main/java/org/labs/dining/items/Programmer.java @@ -7,7 +7,7 @@ public class Programmer extends Thread { private final Spoon leftSpoon; private final Spoon rightSpoon; - public int portionsConsumed = 0; + private int portionsConsumed = 0; public Programmer(String programmerName, Spoon leftSpoon, Spoon rightSpoon) { super(programmerName); @@ -15,6 +15,10 @@ public Programmer(String programmerName, Spoon leftSpoon, Spoon rightSpoon) { this.rightSpoon = rightSpoon; } + public int getPortionsConsumed() { + return portionsConsumed; + } + private synchronized void printMessage(String message) throws InterruptedException { int ms = (int) (Math.random() * 10); System.out.println(Thread.currentThread().getName() + " " + message + " for: " + ms + "ms"); From a5beb9c7d42232f8699e01288ebca8605cdfbc04 Mon Sep 17 00:00:00 2001 From: jbisss Date: Wed, 24 Sep 2025 00:52:03 +0300 Subject: [PATCH 07/11] [feature] some encapsulation --- .../org/labs/dining/DiningProgrammersWorld.java | 8 ++++---- src/main/java/org/labs/dining/SharedContext.java | 16 ++++++++++++---- .../java/org/labs/dining/items/Programmer.java | 4 ++-- src/main/java/org/labs/dining/items/Waiter.java | 8 ++++++-- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/labs/dining/DiningProgrammersWorld.java b/src/main/java/org/labs/dining/DiningProgrammersWorld.java index 53f37c6..3d47350 100644 --- a/src/main/java/org/labs/dining/DiningProgrammersWorld.java +++ b/src/main/java/org/labs/dining/DiningProgrammersWorld.java @@ -5,12 +5,12 @@ public class DiningProgrammersWorld { - public static Programmer[] programmers; - private static final int DEFAULT_PORTIONS_AMOUNT = 1_000_000; private static final int DEFAULT_WAITERS_AMOUNT = 2; private static final int DEFAULT_PROGRAMMERS_AMOUNT = 5; + private static Programmer[] programmers; + public void startDining( Integer portionsAmount, Integer waitersAmount, @@ -24,8 +24,8 @@ public void startDining( programmers = new Programmer[programmersAmountToInitialize]; for (int i = 0; i < programmersAmountToInitialize; i++) { - Spoon leftSpoon = SharedContext.spoons[i]; - Spoon rightSpoon = SharedContext.spoons[(i + 1) % programmersAmountToInitialize]; + Spoon leftSpoon = SharedContext.getSpoon(i); + Spoon rightSpoon = SharedContext.getSpoon((i + 1) % programmersAmountToInitialize); String programmerName = "Programmer_" + i; Programmer currentProgrammer = i == programmersAmountToInitialize - 1 diff --git a/src/main/java/org/labs/dining/SharedContext.java b/src/main/java/org/labs/dining/SharedContext.java index f0b8e38..5c39084 100644 --- a/src/main/java/org/labs/dining/SharedContext.java +++ b/src/main/java/org/labs/dining/SharedContext.java @@ -8,16 +8,24 @@ public class SharedContext { - public static Waiter[] waiters; - public static Spoon[] spoons; - public static BlockingQueue waiterBlockingQueue; + private static Waiter[] waiters; + private static Spoon[] spoons; + private static BlockingQueue waiterBlockingQueue; + + public static Spoon getSpoon(int atIndex) { + return spoons[atIndex]; + } + + public static BlockingQueue getWaiterBlockingQueue() { + return waiterBlockingQueue; + } public static void initialize( int portionsAmount, int waitersAmount, int spoonsAmount ) throws InterruptedException { - Waiter.portionsAmount = portionsAmount; + Waiter.setPortionsAmount(portionsAmount); SharedContext.waiters = new Waiter[waitersAmount]; SharedContext.spoons = new Spoon[spoonsAmount]; diff --git a/src/main/java/org/labs/dining/items/Programmer.java b/src/main/java/org/labs/dining/items/Programmer.java index 0c53869..e77cfff 100644 --- a/src/main/java/org/labs/dining/items/Programmer.java +++ b/src/main/java/org/labs/dining/items/Programmer.java @@ -30,9 +30,9 @@ public void trashTalk() throws InterruptedException { } public boolean requestPortion() throws InterruptedException { - Waiter waiter = SharedContext.waiterBlockingQueue.take(); + Waiter waiter = SharedContext.getWaiterBlockingQueue().take(); boolean portionTaken = waiter.tryTakePortion(); - SharedContext.waiterBlockingQueue.put(waiter); + SharedContext.getWaiterBlockingQueue().put(waiter); return portionTaken; } diff --git a/src/main/java/org/labs/dining/items/Waiter.java b/src/main/java/org/labs/dining/items/Waiter.java index df37529..23afe6a 100644 --- a/src/main/java/org/labs/dining/items/Waiter.java +++ b/src/main/java/org/labs/dining/items/Waiter.java @@ -2,10 +2,14 @@ public class Waiter { - public static int portionsAmount; - private static final Object waitersLock = new Object(); + private static int portionsAmount; + + public static void setPortionsAmount(int portionsAmount) { + Waiter.portionsAmount = portionsAmount; + } + public boolean tryTakePortion() { synchronized (waitersLock) { if (portionsAmount > 0) { From 5698a7b2a61f3393485e2b37512de683cbac1635 Mon Sep 17 00:00:00 2001 From: jbisss Date: Wed, 24 Sep 2025 11:50:35 +0300 Subject: [PATCH 08/11] [feature] tests --- .../labs/dining/DiningProgrammersWorld.java | 6 +- .../org/labs/dining/items/Programmer.java | 2 +- .../dining/DiningProgrammersWorldTest.java | 78 +++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 src/test/java/dining/DiningProgrammersWorldTest.java diff --git a/src/main/java/org/labs/dining/DiningProgrammersWorld.java b/src/main/java/org/labs/dining/DiningProgrammersWorld.java index 3d47350..ed22e60 100644 --- a/src/main/java/org/labs/dining/DiningProgrammersWorld.java +++ b/src/main/java/org/labs/dining/DiningProgrammersWorld.java @@ -9,7 +9,11 @@ public class DiningProgrammersWorld { private static final int DEFAULT_WAITERS_AMOUNT = 2; private static final int DEFAULT_PROGRAMMERS_AMOUNT = 5; - private static Programmer[] programmers; + private Programmer[] programmers; + + public Programmer[] getProgrammers() { + return programmers; + } public void startDining( Integer portionsAmount, diff --git a/src/main/java/org/labs/dining/items/Programmer.java b/src/main/java/org/labs/dining/items/Programmer.java index e77cfff..79b6f76 100644 --- a/src/main/java/org/labs/dining/items/Programmer.java +++ b/src/main/java/org/labs/dining/items/Programmer.java @@ -20,7 +20,7 @@ public int getPortionsConsumed() { } private synchronized void printMessage(String message) throws InterruptedException { - int ms = (int) (Math.random() * 10); + int ms = (int) (Math.random() * 2); System.out.println(Thread.currentThread().getName() + " " + message + " for: " + ms + "ms"); Thread.sleep(ms); } diff --git a/src/test/java/dining/DiningProgrammersWorldTest.java b/src/test/java/dining/DiningProgrammersWorldTest.java new file mode 100644 index 0000000..0ea9278 --- /dev/null +++ b/src/test/java/dining/DiningProgrammersWorldTest.java @@ -0,0 +1,78 @@ +package dining; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.labs.dining.DiningProgrammersWorld; +import org.labs.dining.items.Programmer; + +import java.util.Arrays; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DiningProgrammersWorldTest { + + private static final int SMALL_PORTIONS_AMOUNT_TO_SIMULATE = 1000; + private static final int MEDIUM_PORTIONS_AMOUNT_TO_SIMULATE = 10_000; + private static final int BIG_PORTIONS_AMOUNT_TO_SIMULATE = 100_000; + private static final int LARGE_PORTIONS_AMOUNT_TO_SIMULATE = 1_000_000; + + private static final int WAITERS_AMOUNT_TO_SIMULATE = 2; + + private static final int PROGRAMMERS_AMOUNT_TO_SIMULATE = 5; + + public record DiningProgrammersWorldArgument(int portionsAmount, int waitersAmount, int programmersAmount){} + + private static Stream provideArguments() { + return Stream.of( + Arguments.of(new DiningProgrammersWorldArgument( + SMALL_PORTIONS_AMOUNT_TO_SIMULATE, + WAITERS_AMOUNT_TO_SIMULATE, + PROGRAMMERS_AMOUNT_TO_SIMULATE + ) + ), + Arguments.of(new DiningProgrammersWorldArgument( + MEDIUM_PORTIONS_AMOUNT_TO_SIMULATE, + WAITERS_AMOUNT_TO_SIMULATE, + PROGRAMMERS_AMOUNT_TO_SIMULATE + ) + ), + Arguments.of(new DiningProgrammersWorldArgument( + BIG_PORTIONS_AMOUNT_TO_SIMULATE, + WAITERS_AMOUNT_TO_SIMULATE, + PROGRAMMERS_AMOUNT_TO_SIMULATE + ) + ) + ); + } + + @Test + public void testNoDeadlocks() throws InterruptedException { + new DiningProgrammersWorld().startDining( + LARGE_PORTIONS_AMOUNT_TO_SIMULATE, + WAITERS_AMOUNT_TO_SIMULATE, + PROGRAMMERS_AMOUNT_TO_SIMULATE + ); + } + + @ParameterizedTest + @MethodSource("provideArguments") + public void testNoRaceCondition(DiningProgrammersWorldArgument argument) throws InterruptedException { + DiningProgrammersWorld diningProgrammersWorld = new DiningProgrammersWorld(); + diningProgrammersWorld.startDining( + argument.portionsAmount(), + argument.waitersAmount(), + argument.programmersAmount() + ); + + Programmer[] programmers = diningProgrammersWorld.getProgrammers(); + + int consumedPortionsSum = Arrays.stream(programmers) + .map(Programmer::getPortionsConsumed) + .reduce(0, Integer::sum); + + assertEquals(argument.portionsAmount(), consumedPortionsSum); + } +} From 85fb3816102930c44c469fc4c3c169315461dac3 Mon Sep 17 00:00:00 2001 From: jbisss Date: Thu, 2 Oct 2025 15:33:22 +0300 Subject: [PATCH 09/11] [fix] bigger delay for thread --- src/main/java/org/labs/dining/items/Programmer.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/labs/dining/items/Programmer.java b/src/main/java/org/labs/dining/items/Programmer.java index 79b6f76..5bd7e7d 100644 --- a/src/main/java/org/labs/dining/items/Programmer.java +++ b/src/main/java/org/labs/dining/items/Programmer.java @@ -19,8 +19,14 @@ public int getPortionsConsumed() { return portionsConsumed; } + /** + * Любое действие выводится в консоль, поэтому управление "длительностью" действия + * происходит здесь - в одном месте + *

+ * Выбирается случайное время для задержки, печатается действия и поток "засыпает" на выбранное время + */ private synchronized void printMessage(String message) throws InterruptedException { - int ms = (int) (Math.random() * 2); + int ms = (int) (Math.random() * 100); System.out.println(Thread.currentThread().getName() + " " + message + " for: " + ms + "ms"); Thread.sleep(ms); } From 6c81d354641c258f64b4aa1182840da773f048cd Mon Sep 17 00:00:00 2001 From: jbisss Date: Thu, 2 Oct 2025 15:53:56 +0300 Subject: [PATCH 10/11] [fix] lock to atomic --- src/main/java/org/labs/dining/items/Waiter.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/labs/dining/items/Waiter.java b/src/main/java/org/labs/dining/items/Waiter.java index 23afe6a..ba076a4 100644 --- a/src/main/java/org/labs/dining/items/Waiter.java +++ b/src/main/java/org/labs/dining/items/Waiter.java @@ -1,22 +1,17 @@ package org.labs.dining.items; -public class Waiter { +import java.util.concurrent.atomic.AtomicInteger; - private static final Object waitersLock = new Object(); +public class Waiter { - private static int portionsAmount; + private static AtomicInteger portionsAmount; public static void setPortionsAmount(int portionsAmount) { - Waiter.portionsAmount = portionsAmount; + Waiter.portionsAmount = new AtomicInteger(portionsAmount); } public boolean tryTakePortion() { - synchronized (waitersLock) { - if (portionsAmount > 0) { - portionsAmount--; - return true; - } - return false; - } + int decrementedPortionsAmount = portionsAmount.getAndDecrement(); + return decrementedPortionsAmount > 0; } } From eff83f48a3080a6482d63ac1409dcadfcc01d6d0 Mon Sep 17 00:00:00 2001 From: jbisss Date: Thu, 2 Oct 2025 15:58:18 +0300 Subject: [PATCH 11/11] [fix] take spoons and eating joined actions now --- src/main/java/org/labs/dining/items/Programmer.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/labs/dining/items/Programmer.java b/src/main/java/org/labs/dining/items/Programmer.java index 5bd7e7d..cb61b41 100644 --- a/src/main/java/org/labs/dining/items/Programmer.java +++ b/src/main/java/org/labs/dining/items/Programmer.java @@ -42,16 +42,17 @@ public boolean requestPortion() throws InterruptedException { return portionTaken; } - public void takeSpoons() throws InterruptedException { + public void takeSpoonsAndEat() throws InterruptedException { synchronized (leftSpoon) { printMessage("takes left spoon"); synchronized (rightSpoon) { printMessage("takes right spoon"); + eat(); } } } - public void eat() throws InterruptedException { + private void eat() throws InterruptedException { printMessage("is eating"); this.portionsConsumed++; } @@ -67,8 +68,7 @@ public void run() { trashTalk(); boolean portionTaken = requestPortion(); if (!portionTaken) break; - takeSpoons(); - eat(); + takeSpoonsAndEat(); putSpoons(); } System.out.println("Done " + this.getName() + " with consumed: " + this.portionsConsumed);