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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

# Цели и задачи л/р:
Expand Down
7 changes: 7 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ repositories {
dependencies {
testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")

implementation("org.slf4j:slf4j-api:2.0.9")
implementation("ch.qos.logback:logback-classic:1.4.11")

implementation("com.fasterxml.jackson.core:jackson-databind:2.15.2")
implementation("com.fasterxml.jackson.core:jackson-core:2.15.2")
implementation("com.fasterxml.jackson.core:jackson-annotations:2.15.2")
}

tasks.test {
Expand Down
Empty file modified gradlew
100644 → 100755
Empty file.
27 changes: 26 additions & 1 deletion src/main/java/org/labs/Main.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
package org.labs;

import org.labs.configuration.ConfigLoader;
import org.labs.configuration.SimulationConfig;
import org.labs.simulation.Simulation;
import org.labs.statistics.SimulationStatistics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.labs.simulation.SimulationUtils.createSimulation;
import static org.labs.simulation.SimulationUtils.runSimulation;

public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);

public static void main(String[] args) {
System.out.println("Hello, World!");
logger.info("Starting Dining Programmers Problem Simulation");

try {
SimulationConfig config = ConfigLoader.loadDefaultConfig();
logger.info("Loaded configuration: {}", config);

Simulation simulation = createSimulation(config);

SimulationStatistics statistics = runSimulation(simulation, config);
statistics.printFinalStatistics(simulation.plates());
} catch (Exception e) {
logger.error("Simulation failed", e);
System.exit(1);
}
}
}
33 changes: 33 additions & 0 deletions src/main/java/org/labs/configuration/ConfigLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.labs.configuration;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;

public class ConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(ConfigLoader.class);
private static final ObjectMapper objectMapper = new ObjectMapper();

public static SimulationConfig loadConfig(String configPath) {
try (InputStream inputStream = ConfigLoader.class.getClassLoader().getResourceAsStream(configPath)) {
if (inputStream == null) {
throw new RuntimeException("Configuration file not found: " + configPath);
}

SimulationConfig config = objectMapper.readValue(inputStream, SimulationConfig.class);
logger.info("Loaded simulation configuration: {}", config);
return config;

} catch (IOException e) {
logger.error("Failed to load configuration from {}", configPath, e);
throw new RuntimeException("Failed to load configuration", e);
}
}

public static SimulationConfig loadDefaultConfig() {
return loadConfig("simulation-config.json");
}
}
18 changes: 18 additions & 0 deletions src/main/java/org/labs/configuration/ProgrammerProperties.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.labs.configuration;

import java.util.concurrent.ThreadLocalRandom;

public record ProgrammerProperties(
int minEatingTime,
int maxEatingTime,
int minChitChatTime,
int maxChitChatTime
) {
public long getEatingTime() {
return ThreadLocalRandom.current().nextLong(minEatingTime, maxEatingTime);
}

public long getChitChatTime() {
return ThreadLocalRandom.current().nextLong(minChitChatTime, maxChitChatTime);
}
}
10 changes: 10 additions & 0 deletions src/main/java/org/labs/configuration/SimulationConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.labs.configuration;

public record SimulationConfig(
int numberOfProgrammers,
int numberOfWaiters,
int totalFoodPortions,
ProgrammerProperties programmerProperties,
WaiterProperties waiterProperties
) {
}
12 changes: 12 additions & 0 deletions src/main/java/org/labs/configuration/WaiterProperties.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.labs.configuration;

import java.util.concurrent.ThreadLocalRandom;

public record WaiterProperties(
int minServingTime,
int maxServingTime
) {
public long getServingTime() {
return ThreadLocalRandom.current().nextLong(minServingTime, maxServingTime);
}
}
46 changes: 46 additions & 0 deletions src/main/java/org/labs/model/Plate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.labs.model;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

public class Plate implements Comparable<Plate> {
private final AtomicReference<PlateState> plateState;
private final AtomicInteger numOfRefills;
private final int programmerId;

public Plate(int programmerId) {
this.programmerId = programmerId;
this.plateState = new AtomicReference<>(PlateState.EMPTY);
this.numOfRefills = new AtomicInteger(0);
}

public PlateState getState() {
return plateState.get();
}

public int getNumOfRefills() {
return numOfRefills.get();
}

public int getProgrammerId() {
return programmerId;
}

public void refill() {
plateState.set(PlateState.FULL);
numOfRefills.incrementAndGet();
}

public void finish() {
plateState.set(PlateState.EMPTY);
}

public void order() {
plateState.set(PlateState.ORDERED);
}

@Override
public int compareTo(Plate o) {
return Integer.compare(this.numOfRefills.get(), o.numOfRefills.get());
}
}
7 changes: 7 additions & 0 deletions src/main/java/org/labs/model/PlateState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.labs.model;

public enum PlateState {
EMPTY,
ORDERED,
FULL
}
81 changes: 81 additions & 0 deletions src/main/java/org/labs/model/Programmer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.labs.model;

import org.labs.configuration.ProgrammerProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Programmer implements Runnable {
private final Logger log = LoggerFactory.getLogger(Programmer.class);
private final ProgrammerProperties properties;
private final Restaurant restaurant;
private final Plate plate;
private final Spoon smallerSpoon;
private final Spoon biggerSpoon;
private final Integer id;

public Programmer(
ProgrammerProperties properties,
Restaurant restaurant,
Plate plate,
Spoon smallerSpoon,
Spoon biggerSpoon,
Integer id
) {
this.properties = properties;
this.restaurant = restaurant;
this.plate = plate;
this.smallerSpoon = smallerSpoon;
this.biggerSpoon = biggerSpoon;
this.id = id;
}

@Override
public void run() {
try {
haveDinner();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}

public void haveDinner() throws InterruptedException {
while (restaurant.isServing() || plate.getState() == PlateState.FULL) {
restaurant.makeOrder(plate);

if (plate.getState() != PlateState.FULL) {
continue;
}

eat();
chitChat();
}
}

public void eat() throws InterruptedException {
try {
smallerSpoon.take();
log.info("Programmer with id [{}] acquired small spoon with id [{}]", id, smallerSpoon.getId());

biggerSpoon.take();
log.info("Programmer with id [{}] acquired bigger spoon with id [{}]", id, biggerSpoon.getId());

Thread.sleep(properties.getEatingTime());
plate.finish();
log.info("Programmer with id [{}] finished plate number [{}]", id, plate.getNumOfRefills());
} finally {
biggerSpoon.put();
log.info("Programmer with id [{}] put bigger spoon with id [{}]", id, biggerSpoon.getId());

smallerSpoon.put();
log.info("Programmer with id [{}] put smaller spoon with id [{}]", id, smallerSpoon.getId());
}
}

public void chitChat() throws InterruptedException {
Thread.sleep(properties.getChitChatTime());
}

public Plate getPlate() {
return plate;
}
}
43 changes: 43 additions & 0 deletions src/main/java/org/labs/model/Restaurant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.labs.model;

import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Restaurant {
private final Logger log = LoggerFactory.getLogger(Restaurant.class);
private final PriorityBlockingQueue<Plate> platesToServe;
private final AtomicInteger portionsLeft;

public Restaurant(PriorityBlockingQueue<Plate> platesToServe, int maxPortions) {
this.platesToServe = platesToServe;
this.portionsLeft = new AtomicInteger(maxPortions);
}

public void makeOrder(Plate plate) {
if (plate.getState() == PlateState.EMPTY) {
plate.order();
log.info("Programmer with id [{}] ordered new portion", plate.getProgrammerId());

platesToServe.add(plate);
}
}

public boolean hasPlates() {
return !platesToServe.isEmpty();
}

public Plate getPlate() {
return platesToServe.poll();
}

public boolean takeSoup() {
return portionsLeft.getAndUpdate(portionCount -> portionCount > 0 ? portionCount - 1 : 0) > 0;
}

public boolean isServing() {
return portionsLeft.get() > 0;
}
}
25 changes: 25 additions & 0 deletions src/main/java/org/labs/model/Spoon.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.labs.model;

import java.util.concurrent.locks.ReentrantLock;

public class Spoon {
private final int id;
private final ReentrantLock lock;

public Spoon(int id) {
this.id = id;
this.lock = new ReentrantLock();
}

public int getId() {
return id;
}

public void take() {
lock.lock();
}

public void put() {
lock.unlock();
}
}
51 changes: 51 additions & 0 deletions src/main/java/org/labs/model/Waiter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.labs.model;

import org.labs.configuration.WaiterProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Waiter implements Runnable {
private final Logger log = LoggerFactory.getLogger(Waiter.class);
private final WaiterProperties properties;
private final Restaurant restaurant;
private final int id;

public Waiter(
WaiterProperties properties,
Restaurant restaurant,
int id
) {
this.properties = properties;
this.restaurant = restaurant;
this.id = id;
}

@Override
public void run() {
try {
serveProgrammers();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}

public void serveProgrammers() throws InterruptedException {
while (restaurant.isServing() || restaurant.hasPlates()) {
Plate plate = restaurant.getPlate();
if (plate == null) {
continue;
}

Thread.sleep(properties.getServingTime());

if (restaurant.takeSoup()) {
plate.refill();
log.info(
"Waiter with id [{}] refilled plate for programmer with id [{}]",
id,
plate.getProgrammerId()
);
}
}
}
}
Loading