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: 4 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

112 changes: 112 additions & 0 deletions .idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file modified gradlew
100644 → 100755
Empty file.
27 changes: 24 additions & 3 deletions src/main/java/org/labs/Main.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
package org.labs;

public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");

public static void main(String[] args) throws InterruptedException {
startDinner(7, 2, 1000);
}

public static void startDinner(int progsCount, int waitersCount, int portions) throws InterruptedException {
SpoonManager spoonManager = new SpoonManager(progsCount);
Programmer[] programmers = new Programmer[progsCount];
WaiterService waiterService = new WaiterService(waitersCount, portions);
for (int i = 0; i < progsCount; i++) {
programmers[i] = new Programmer(i, spoonManager, waiterService);
programmers[i].start();
}

int total = 0;
for (int i = 0; i < progsCount; i++) {
programmers[i].join();
total += programmers[i].getEaten();
}

waiterService.shutdown();
System.out.println("TOTAL EATEN " + total);
}
}
}

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

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class Programmer extends Thread {

private final int id;
private final SpoonManager spoonManager;
private final WaiterService waiterService;

private boolean hasPortion;
private int eaten = 0;

public Programmer(int id, SpoonManager manager, WaiterService waiterService) {
this.id = id;
this.spoonManager = manager;
this.waiterService = waiterService;
this.hasPortion = false;
}

@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
code();

hasPortion = requestFoodWithTimeout(eaten);
if (!hasPortion) {
break;
}

spoonManager.aquireSpoons(id);
try {
eat();
} finally {
spoonManager.releaseSpoons(id);
}
}
System.out.println("Programmer-" + id + " has eaten " + eaten + " portions");
}

public int getEaten() {
return eaten;
}

private boolean requestFoodWithTimeout(int eaten) {
try {
CompletableFuture<Boolean> getFoodFuture = waiterService.requestFood(eaten);
return getFoodFuture.get(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
} catch (ExecutionException e) {
System.err.println("Waiter failed to bring new portion: " + e.getCause());
return false;
} catch (TimeoutException e) {
System.err.println("No response from waiter");
return false;
}
}

private void eat() {
try {
Thread.sleep(TimeUtil.randomTime(TimeUtil.MIN_WAIT_TIME, TimeUtil.MAX_WAIT_TIME));
eaten++;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}

private void code() {
try {
Thread.sleep(TimeUtil.randomTime(TimeUtil.MIN_WAIT_TIME, TimeUtil.MAX_WAIT_TIME));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
37 changes: 37 additions & 0 deletions src/main/java/org/labs/SpoonManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.labs;

import java.util.concurrent.locks.ReentrantLock;

public class SpoonManager {
private final int spoonCount;
private final ReentrantLock[] issued;

public SpoonManager(int spoonCount) {
this.spoonCount = spoonCount;
this.issued = new ReentrantLock[spoonCount];
for (int i = 0; i < spoonCount; i++) {
issued[i] = new ReentrantLock();
}
}

public void aquireSpoons(int programmerId) {
int leftSpoonIdx = leftSpoonIdx(programmerId);
int rightSpoonIdx = rightSpoonIdx(programmerId);

issued[Math.min(leftSpoonIdx, rightSpoonIdx)].lock();
issued[Math.max(leftSpoonIdx, rightSpoonIdx)].lock();
}

public void releaseSpoons(int programmerId) {
issued[leftSpoonIdx(programmerId)].unlock();
issued[rightSpoonIdx(programmerId)].unlock();
}

private int rightSpoonIdx(int programmerId) {
return programmerId;
}

private int leftSpoonIdx(int programmerId) {
return (programmerId + 1) % spoonCount;
}
}
14 changes: 14 additions & 0 deletions src/main/java/org/labs/TimeUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.labs;

public class TimeUtil {
public static final Integer MIN_WAIT_TIME = 1;
public static final Integer MAX_WAIT_TIME = 3;

public static long randomTime(int min, int max) {
return min + (long)(Math.random() * (max - min));
}

private TimeUtil() {
throw new UnsupportedOperationException();
}
}
35 changes: 35 additions & 0 deletions src/main/java/org/labs/Waiter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.labs;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class Waiter extends Thread {

private final int id;
private final BlockingQueue<WaiterTask> taskQueue;

public Waiter(int id, BlockingQueue<WaiterTask> taskQueue) {
this.id = id;
this.taskQueue = taskQueue;
}

@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
WaiterTask task = taskQueue.poll(1, TimeUnit.SECONDS);
if (task != null) {
task.run();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
System.out.printf("Waiter-%d is shutting down%n", id);
}

public void stopWorking() {
this.interrupt();
}
}
51 changes: 51 additions & 0 deletions src/main/java/org/labs/WaiterService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.labs;

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

/**
* Сервис для обслуживания программистов
*/
public class WaiterService {
private final AtomicInteger portions;
private final PriorityBlockingQueue<WaiterTask> blockingQueue;
private final Waiter[] waiters;

public WaiterService(int waitersCount, int portionCount) {
blockingQueue = new PriorityBlockingQueue<>();
portions = new AtomicInteger(portionCount);
waiters = new Waiter[waitersCount];
for (int i = 0; i < waitersCount; i++) {
waiters[i] = new Waiter(i, blockingQueue);
}

startWaiters();
}

public CompletableFuture<Boolean> requestFood(int eatenBefore) throws InterruptedException {
CompletableFuture<Boolean> future = new CompletableFuture<>();
blockingQueue.offer(new WaiterTask(eatenBefore, portions, future));
return future;
}

private void startWaiters() {
for (Waiter waiter : waiters) {
waiter.start();
}
}

public void shutdown() {
for (Waiter waiter : waiters) {
waiter.stopWorking();
}

for (Waiter waiter : waiters) {
try {
waiter.join(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
Loading