Skip to content
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,7 @@ bin/
.vscode/

### Mac OS ###
.DS_Store
.DS_Store

### Log files ###
*.log
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
21 changes: 21 additions & 0 deletions src/main/java/org/labs/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.labs;

import org.labs.restaurant.Restaurant;

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());
}
}
}
10 changes: 10 additions & 0 deletions src/main/java/org/labs/Constants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.labs;

public class Constants {
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;
}
7 changes: 0 additions & 7 deletions src/main/java/org/labs/Main.java

This file was deleted.

19 changes: 19 additions & 0 deletions src/main/java/org/labs/logger/ConsoleLogger.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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) {
final var prefix = !name.isEmpty() ? name + " " : "";
System.out.println(prefix + String.join(" ", messages));
}
}
5 changes: 5 additions & 0 deletions src/main/java/org/labs/logger/Logger.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.labs.logger;

public interface Logger {
void log(String... messages);
}
76 changes: 76 additions & 0 deletions src/main/java/org/labs/programmer/Programmer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.labs.programmer;

import java.util.Random;

import org.labs.Constants;
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 eatenPortions = 0;

private final WaiterService waiters;
private final Spoon firstSpoon;
private final Spoon secondSpoon;

public Programmer(Integer id, WaiterService waiters, Spoon leftSpoon, Spoon rightSpoon) {
super("Программист " + id);

this.id = id;
this.waiters = waiters;

if (leftSpoon.getId() < rightSpoon.getId()) {
this.firstSpoon = leftSpoon;
this.secondSpoon = rightSpoon;
} else {
this.firstSpoon = rightSpoon;
this.secondSpoon = leftSpoon;
}

this.logger = new ConsoleLogger(getName());
}

@Override
public void run() {
try {
logger.log("начал есть");
while (true) {

logger.log("просит новую порцию");
try {
if (!waiters.getPortion(getName())) {
break;
}
} catch (Exception e) {
break;
}

firstSpoon.take(getName());
secondSpoon.take(getName());

Thread.sleep(random.nextInt(Constants.MAX_EATING_TIME_MS));
eatenPortions++;
logger.log("доел порцию");

secondSpoon.put(getName());
firstSpoon.put(getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public Integer getProgrammerId() {
return id;
}

public Integer getEatenPortions() {
return eatenPortions;
}
}
6 changes: 6 additions & 0 deletions src/main/java/org/labs/restaurant/DinnerInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.labs.restaurant;

import java.util.Map;

public record DinnerInfo(Map<Integer, Integer> programmersEatenPortions, Map<Integer, Integer> waitersServedPortions) {
}
61 changes: 61 additions & 0 deletions src/main/java/org/labs/restaurant/Restaurant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.labs.restaurant;

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;
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<Spoon> spoons = new ArrayList<>();
private final List<Programmer> 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();
}

logger.log("начал ужин");

for (var p : programmers) {
try {
p.join();
} catch (InterruptedException e) {
}
}

logger.log("закончил ужин");
}

public DinnerInfo getDinnerInfo() {
Map<Integer, Integer> programmersEatenPortions = programmers.stream()
.collect(Collectors.toMap(Programmer::getProgrammerId, Programmer::getEatenPortions));
Map<Integer, Integer> waitersServedPortions = waiters.getServedPortions();
return new DinnerInfo(programmersEatenPortions, waitersServedPortions);
}
}
33 changes: 33 additions & 0 deletions src/main/java/org/labs/spoon/Spoon.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.labs.spoon;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.labs.logger.ConsoleLogger;
import org.labs.logger.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);
}

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;
}
}
35 changes: 35 additions & 0 deletions src/main/java/org/labs/waiter/Waiter.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
51 changes: 51 additions & 0 deletions src/main/java/org/labs/waiter/WaiterService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
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.logger.ConsoleLogger;
import org.labs.logger.Logger;

public class WaiterService {
private final Logger logger;

private final AtomicInteger portionsCount;

private final List<Waiter> waiters;
private final AtomicInteger nextWaiter;

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<Integer, Integer> getServedPortions() {
return waiters.stream().collect(Collectors.toMap(Waiter::getId, Waiter::getServedPortions));
}
}
13 changes: 13 additions & 0 deletions src/test/java/org/labs/TestConstants.java
Original file line number Diff line number Diff line change
@@ -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;
}
Loading