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 # Цели и задачи л/р: diff --git a/src/main/java/org/labs/lab1/Developer.java b/src/main/java/org/labs/lab1/Developer.java new file mode 100644 index 0000000..9d207ff --- /dev/null +++ b/src/main/java/org/labs/lab1/Developer.java @@ -0,0 +1,71 @@ +package org.labs.lab1; + +import java.util.Queue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ThreadLocalRandom; + +public class Developer implements Comparable, Runnable { + + private final int id; + private final Spoon leftSpoon; + private final Spoon rightSpoon; + private final BlockingQueue hungryDevs; + private int eaten = 0; + + public Developer(int id, Spoon leftSpoon, Spoon rightSpoon, BlockingQueue hungryDevs) { + this.id = id; + this.leftSpoon = leftSpoon; + this.rightSpoon = rightSpoon; + this.hungryDevs = hungryDevs; + } + + @Override + public void run() { + while (Lunch.REMAINING_FOOD.get() > 0) { + try { + Thread.sleep(ThreadLocalRandom.current().nextInt(20, 60)); + } catch (InterruptedException e) { + System.out.println("Программиста " + id + " прервали на обсуждении."); + return; + } + + hungryDevs.add(this); + } + } + + public void eat() { + Spoon first = leftSpoon; + Spoon second = rightSpoon; + if (first.id() > second.id()) { + Spoon tmp = first; + first = second; + second = tmp; + } + + try { + synchronized (first) { + synchronized (second) { + eaten++; + System.out.println("Программист " + id + ". Всего съел: " + eaten); + Thread.sleep(ThreadLocalRandom.current().nextInt(60, 140)); + } + } + } catch (InterruptedException e) { + System.out.println("Программиста " + id + " прервали на поедании блюда #" + eaten); + return; + } + + System.out.println("Программист " + id + " закончил обед. Съел порций: " + eaten); + } + + @Override + public int compareTo(Developer o) { + if (this.eaten > o.eaten) { + return 1; + } else if (this.eaten < o.eaten) { + return -1; + } else { + return 0; + } + } +} diff --git a/src/main/java/org/labs/lab1/Lunch.java b/src/main/java/org/labs/lab1/Lunch.java new file mode 100644 index 0000000..24c35b4 --- /dev/null +++ b/src/main/java/org/labs/lab1/Lunch.java @@ -0,0 +1,94 @@ +package org.labs.lab1; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import org.labs.Main; + +public class Lunch { + + public static AtomicInteger REMAINING_FOOD; + public static BlockingQueue KITCHEN; + + public static void main(String[] args) { + Properties prop = new Properties(); + + try (InputStream input = Main.class.getClassLoader().getResourceAsStream("application.properties")) { + if (input == null) { + System.out.println("Файл application.properties не найден в ресурсах!"); + return; + } + prop.load(input); + } catch (FileNotFoundException e) { + System.out.println("Файл не найден"); + } catch (IOException e) { + System.out.println(e.getMessage()); + System.out.println("Проблемы ввода/вывода"); + } + + int devCount = Integer.parseInt(prop.getProperty("devCount", "7")); + int waiterCount = Integer.parseInt(prop.getProperty("waiterCount", "2")); + int totalFood = Integer.parseInt(prop.getProperty("totalFood", "1000")); + String threadPoolTypeStr = prop.getProperty("threadPoolType", "SINGLE"); + ThreadPoolType threadPoolType = ThreadPoolType.valueOf(threadPoolTypeStr); + + Lunch lunch = new Lunch(); + lunch.startLunch(devCount, waiterCount, totalFood, threadPoolType); + } + + private void startLunch(int devCount, int waiterCount, int totalFood, ThreadPoolType threadPoolType) { + REMAINING_FOOD = new AtomicInteger(totalFood); + KITCHEN = new PriorityBlockingQueue<>(devCount); + + ExecutorService executorService = switch (threadPoolType) { + case FIXED -> Executors.newFixedThreadPool(devCount + waiterCount); + case CACHED -> Executors.newCachedThreadPool(); + case WORK_STEALING -> Executors.newWorkStealingPool(devCount + waiterCount); + case SINGLE -> Executors.newSingleThreadExecutor(); + }; + + long start = System.currentTimeMillis(); + + Waiter[] waiters = new Waiter[waiterCount]; + for (int i = 0; i < waiterCount; i++) { + waiters[i] = new Waiter(i, KITCHEN); + executorService.execute(waiters[i]); + } + + Developer[] devs = getDevs(devCount); + for (Developer dev : devs) { + executorService.execute(dev); + } + + executorService.shutdown(); + try { + executorService.awaitTermination(5, TimeUnit.MINUTES); + } catch (InterruptedException e) { + System.out.println("Ожидание окончания работы было прервано"); + } + + long workingTime = System.currentTimeMillis() - start; + System.out.println(threadPoolType + " thread pool отработал за " + workingTime + " мс"); + } + + private static Developer[] getDevs(int devCount) { + Developer[] devs = new Developer[devCount]; + + Spoon[] spoons = new Spoon[devCount]; + for (int i = 0; i < devCount; i++) { + spoons[i] = new Spoon(i); + } + + for (int i = 0; i < devCount; i++) { + Spoon left = spoons[i]; + Spoon right = spoons[(i + 1) % devCount]; + devs[i] = new Developer(i, left, right, KITCHEN); + } + + return devs; + } +} diff --git a/src/main/java/org/labs/lab1/Spoon.java b/src/main/java/org/labs/lab1/Spoon.java new file mode 100644 index 0000000..142b5a4 --- /dev/null +++ b/src/main/java/org/labs/lab1/Spoon.java @@ -0,0 +1,4 @@ +package org.labs.lab1; + +public record Spoon(int id) { +} diff --git a/src/main/java/org/labs/lab1/ThreadPoolType.java b/src/main/java/org/labs/lab1/ThreadPoolType.java new file mode 100644 index 0000000..ac0a4a3 --- /dev/null +++ b/src/main/java/org/labs/lab1/ThreadPoolType.java @@ -0,0 +1,8 @@ +package org.labs.lab1; + +public enum ThreadPoolType { + FIXED, + CACHED, + WORK_STEALING, + SINGLE +} diff --git a/src/main/java/org/labs/lab1/Waiter.java b/src/main/java/org/labs/lab1/Waiter.java new file mode 100644 index 0000000..4c13299 --- /dev/null +++ b/src/main/java/org/labs/lab1/Waiter.java @@ -0,0 +1,31 @@ +package org.labs.lab1; +import java.util.concurrent.BlockingQueue; + +public class Waiter implements Runnable { + + private final int id; + private final BlockingQueue hungryDevs; + + public Waiter(int id, BlockingQueue hungryDevs) { + this.id = id; + this.hungryDevs = hungryDevs; + } + + @Override + public void run() { + int next = Lunch.REMAINING_FOOD.getAndDecrement(); + + while (next > 0) { + try { + Developer hungryDev = hungryDevs.take(); + hungryDev.eat(); + } catch (InterruptedException e) { + System.out.println("Вставка официантом " + id + " блюда #" + next + " была прервана"); + Thread.currentThread().interrupt(); + return; + } + + next = Lunch.REMAINING_FOOD.getAndDecrement(); + } + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..f52b525 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,4 @@ +devCount=7 +waiterCount=2 +totalFood=1000 +threadPoolType=WORK_STEALING \ No newline at end of file