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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ build/
!**/src/test/**/build/

### IntelliJ IDEA ###
.idea/
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
Expand Down Expand Up @@ -39,4 +40,5 @@ bin/
.vscode/

### Mac OS ###
.DS_Store
.DS_Store
/.idea/
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,21 @@
* Использование системы сборки Gradle
* Код должен быть отлажен и протестирован

# Дедлайн 08.10.2025 23:59
# Дедлайн 08.10.2025 23:59

Решение:

Для предотвращения deadlockа пронумеруем ложки от 0 до n - 1.
Сначала программист будет дожидаться ложки с меньшим номером, затем с большим.

Официанты будут реализованы как пул потоков в ExecutorService.
При запросе еды программист блокируется, пока ему ее не принесут.
Полученная future вернет true, если еда была выдана, и false, если она закончилась.
При false поток программиста завершает работу.

Step 1 - без приоритетов


Step 2 - с приоритетами:

Когда программист поел, у его соседей повышается приоритет, а свой сбрасывается. Если сосед с высшим приоритетом ждет ложку, текущий программист не имеет права ее брать.
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id("java")
id("io.freefair.lombok") version "9.1.0"
}

group = "org.labs"
Expand All @@ -16,4 +17,4 @@ dependencies {

tasks.test {
useJUnitPlatform()
}
}
Empty file modified gradlew
100644 → 100755
Empty file.
29 changes: 29 additions & 0 deletions results.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
OrderedSpoonService + SimpleWaiterService
Total time: 23217 ms
Delta: 55.76%
[8.16, 9.14, 9.35, 9.65, 10.04, 10.34, 10.9, 11.54, 12.71, 8.17]

OrderedSpoonService + SimpleWaiterService (discuss time > eat time)
Total time: 12609 ms
Delta: 4.57%
[9.85, 9.97, 9.88, 9.85, 9.96, 9.94, 10.22, 10.14, 10.3, 9.89]

LimitedSpoonService + SimpleWaiterService
Total time: 72404 ms
Delta: 0.20%
[10.0, 10.0, 9.99, 9.99, 10.0, 10.01, 10.01, 10.0, 10.0, 10.0]

OrderedSpoonService + PrioritizedWaiterService (10%)
Total time: 23475 ms
Delta: 59.88%
[8.11, 9.01, 9.31, 9.73, 9.94, 10.38, 10.92, 11.55, 12.95, 8.1]

OrderedSpoonService + PrioritizedWaiterService (1%)
Total time: 27421 ms
Delta: 10.88%
[9.28, 9.77, 9.95, 10.27, 10.29, 10.29, 10.29, 10.29, 10.29, 9.28]

LimitedSpoonService + PrioritizedWaiterService (1%)
Total time: 79718 ms
Delta: 0.20%
[10.0, 10.0, 10.0, 9.99, 10.01, 10.01, 10.0, 10.0, 10.0, 9.99]
42 changes: 42 additions & 0 deletions src/main/java/org/labs/Cafe.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.labs;

import java.util.List;
import java.util.stream.IntStream;

public class Cafe {

private final WaiterService waiterService;
private final List<Programmer> programmers;
private final List<Thread> programmerThreads;

public Cafe(CafeConfig config, WaiterService waiterService, SpoonService spoonService) {
this.waiterService = waiterService;
programmers = IntStream.range(0, config.getProgrammersNumber())
.mapToObj(id -> new Programmer(waiterService, spoonService, id, config))
.toList();

programmerThreads = programmers.stream().map(Thread::new).toList();
}

public void start() {
programmerThreads.forEach(Thread::start);
}

public void join() {
for (var p : programmerThreads) {
if (Thread.currentThread().isInterrupted()) {
p.interrupt();
}
try {
p.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
waiterService.shutdown();
}

public List<Integer> getFoodDistribution() {
return programmers.stream().map(Programmer::getEatenFood).toList();
}
}
23 changes: 23 additions & 0 deletions src/main/java/org/labs/CafeConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.labs;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class CafeConfig {
private final int programmersNumber;
private final int waitersNumber;
private final int totalFood;
private final int spoonCount;
@Builder.Default
private final int minDiscussTimeInMillis = 1;
@Builder.Default
private final int maxDiscussTimeInMillis = 5;
@Builder.Default
private final int minEatTimeInMillis = 5;
@Builder.Default
private final int maxEatTimeInMillis = 10;
private final Integer deltaPercentage; // Optional, for PrioritizedWaiterService
}

40 changes: 40 additions & 0 deletions src/main/java/org/labs/LimitedSpoonService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.labs;

import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;

/**
* Позволяет взять левую ложку n - 1 раз.
* Предотвращает ситуацию, когда все n программистов взяли левую ложку.
*/
public class LimitedSpoonService implements SpoonService {

private final int spoonCount;
private final List<ReentrantLock> spoonLocks;
private final Semaphore semaphore;

public LimitedSpoonService(int spoonCount) {
this.spoonCount = spoonCount;
this.spoonLocks = IntStream.range(0, spoonCount)
.mapToObj(i -> new ReentrantLock())
.toList();
this.semaphore = new Semaphore(spoonCount - 1, true);
}

@Override
public void acquireSpoons(int id) throws InterruptedException {
semaphore.acquire();
spoonLocks.get(id).lock();
spoonLocks.get((id + 1) % spoonCount).lock();
}

@Override
public void releaseSpoons(int id) {
spoonLocks.get((id + 1) % spoonCount).unlock();
spoonLocks.get(id).unlock();
semaphore.release();
}

}
167 changes: 165 additions & 2 deletions src/main/java/org/labs/Main.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,170 @@
package org.labs;

import java.io.FileNotFoundException;
import java.io.PrintWriter;

public class Main {

// SpoonService:
// OrderedSpoonService
// LimitedSpoonService
// WaiterService:
// SimpleWaiterService
// PrioritizedWaiterService

public static void main(String[] args) {
System.out.println("Hello, World!");
PrintWriter fileOut;
try {
fileOut = new PrintWriter("results.txt");
} catch (FileNotFoundException e) {
System.err.println("Failed to create results.txt: " + e.getMessage());
return;
}

fileOut.println("OrderedSpoonService + SimpleWaiterService");
CafeConfig config = CafeConfig.builder()
.programmersNumber(10)
.waitersNumber(4)
.totalFood(10000)
.spoonCount(10)
.minDiscussTimeInMillis(1)
.maxDiscussTimeInMillis(5)
.minEatTimeInMillis(5)
.maxEatTimeInMillis(10)
.build();
test(
fileOut,
config,
new OrderedSpoonService(config.getSpoonCount()),
new SimpleWaiterService(config.getWaitersNumber(), config.getTotalFood())
);


fileOut.println("\nOrderedSpoonService + SimpleWaiterService (discuss time > eat time)");
config = CafeConfig.builder()
.programmersNumber(10)
.waitersNumber(4)
.totalFood(10000)
.spoonCount(10)
.minDiscussTimeInMillis(5)
.maxDiscussTimeInMillis(10)
.minEatTimeInMillis(1)
.maxEatTimeInMillis(5)
.build();
test(
fileOut,
config,
new OrderedSpoonService(config.getSpoonCount()),
new SimpleWaiterService(config.getWaitersNumber(), config.getTotalFood())
);

fileOut.println("\nLimitedSpoonService + SimpleWaiterService");
config = CafeConfig.builder()
.programmersNumber(10)
.waitersNumber(4)
.totalFood(10000)
.spoonCount(10)
.minDiscussTimeInMillis(1)
.maxDiscussTimeInMillis(5)
.minEatTimeInMillis(5)
.maxEatTimeInMillis(10)
.build();
test(
fileOut,
config,
new LimitedSpoonService(config.getSpoonCount()),
new SimpleWaiterService(config.getWaitersNumber(), config.getTotalFood())
);

fileOut.println("\nOrderedSpoonService + PrioritizedWaiterService (10%)");
config = CafeConfig.builder()
.programmersNumber(10)
.waitersNumber(4)
.totalFood(10000)
.spoonCount(10)
.minDiscussTimeInMillis(1)
.maxDiscussTimeInMillis(5)
.minEatTimeInMillis(5)
.maxEatTimeInMillis(10)
.deltaPercentage(10)
.build();
test(
fileOut,
config,
new OrderedSpoonService(config.getSpoonCount()),
new PrioritizedWaiterService(config.getProgrammersNumber(), config.getWaitersNumber(), config.getTotalFood(), config.getDeltaPercentage())
);

fileOut.println("\nOrderedSpoonService + PrioritizedWaiterService (1%)");
System.out.println("OrderedSpoonService + PrioritizedWaiterService (1%)");
config = CafeConfig.builder()
.programmersNumber(10)
.waitersNumber(4)
.totalFood(10000)
.spoonCount(10)
.minDiscussTimeInMillis(1)
.maxDiscussTimeInMillis(5)
.minEatTimeInMillis(5)
.maxEatTimeInMillis(10)
.deltaPercentage(1)
.build();
test(
fileOut,
config,
new OrderedSpoonService(config.getSpoonCount()),
new PrioritizedWaiterService(config.getProgrammersNumber(), config.getWaitersNumber(), config.getTotalFood(), config.getDeltaPercentage())
);

fileOut.println("\nLimitedSpoonService + PrioritizedWaiterService (1%)");
config = CafeConfig.builder()
.programmersNumber(10)
.waitersNumber(4)
.totalFood(10000)
.spoonCount(10)
.minDiscussTimeInMillis(1)
.maxDiscussTimeInMillis(5)
.minEatTimeInMillis(5)
.maxEatTimeInMillis(10)
.deltaPercentage(1)
.build();
test(
fileOut,
config,
new LimitedSpoonService(config.getSpoonCount()),
new PrioritizedWaiterService(config.getProgrammersNumber(), config.getWaitersNumber(), config.getTotalFood(), config.getDeltaPercentage())
);

fileOut.close();
}

private static void test(
PrintWriter fileOut,
CafeConfig config,
SpoonService spoonService,
WaiterService waiterService
) {
Cafe cafe = new Cafe(config, waiterService, spoonService);

long startTime = System.currentTimeMillis();

cafe.start();
cafe.join();

long endTime = System.currentTimeMillis();

long totalTime = endTime - startTime;

var distribution = cafe.getFoodDistribution();
var sum = distribution.stream().mapToInt(Integer::intValue).sum();
var percentage = distribution.stream().map(value -> 100.0 * value / sum).toList();
var minFood = distribution.stream().min(Integer::compare).get();
var maxFood = distribution.stream().max(Integer::compare).get();
var delta = 100.0 * (maxFood - minFood) / minFood;

fileOut.println("Total time: " + totalTime + " ms");
fileOut.printf("Delta: %.2f", delta);
fileOut.println("%");
fileOut.println(percentage);
fileOut.flush();
}
}
}
Loading