diff --git a/lab-02/DocksAndHobos/DocksAndHobos.iml b/lab-02/DocksAndHobos/DocksAndHobos.iml new file mode 100644 index 0000000..bd0c28c --- /dev/null +++ b/lab-02/DocksAndHobos/DocksAndHobos.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/lab-02/DocksAndHobos/config.json b/lab-02/DocksAndHobos/config.json new file mode 100644 index 0000000..35e3f96 --- /dev/null +++ b/lab-02/DocksAndHobos/config.json @@ -0,0 +1,22 @@ +{ + "generating_time": 15, + "ship_capacity_min": 20, + "ship_capacity_max": 100, + "max_ships": 4, + "unloading_speed": 7, + "dock_capacity": 100, + "hobos": 10, + "stealing_time": 2, + "eating_time": 3, + "cargo_types" : [ + "meat", + "fish", + "oil" + ], + + "ingredients_count" : [ + 7, + 2, + 10 + ] +} \ No newline at end of file diff --git a/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/Controller.java b/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/Controller.java new file mode 100644 index 0000000..bcfe8e5 --- /dev/null +++ b/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/Controller.java @@ -0,0 +1,63 @@ +package by.DaniilDomnin.doks_and_hobos; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Controller { + Controller(ShipGenerator generator, Docks docks, long generating_time, long max_ships) throws InterruptedException { + this.generator = generator; + this.generating_time = generating_time; + this.max_ships = max_ships; + this.docks = docks; + ship_count = new AtomicInteger(); + consoleLOgger = Logger.getLogger(" "); + Thread thread = new Thread(docks::StartStealing); + thread.start(); + Generating(); + } + + private void Generating() throws InterruptedException { + while (true) { + if (ship_count.get() < max_ships) { + Ship ship = generator.GenerateShip(); + consoleLOgger.log(Level.INFO, "Generate new ship"); + Thread thread = new Thread(()->{ + try { + Uploading(ship); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + thread.start(); + ship_count.incrementAndGet(); + } else { + consoleLOgger.log(Level.INFO, "The ship sank in the tunnel"); + } + + Thread.sleep(generating_time * 1000); + } + } + + private void Uploading(Ship ship) throws InterruptedException { + docks.Unloading(ship); + ship_count.decrementAndGet(); + } + + public static Logger GetConsoleLogger () { + return consoleLOgger; + } + + private final ShipGenerator generator; + + private static Logger consoleLOgger; + + private final AtomicInteger ship_count; + private final long generating_time; + + private final long max_ships; + + private final Docks docks; +} diff --git a/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/Docks.java b/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/Docks.java new file mode 100644 index 0000000..550dbd9 --- /dev/null +++ b/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/Docks.java @@ -0,0 +1,125 @@ +package by.DaniilDomnin.doks_and_hobos; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; + +public class Docks { + Docks(long dock_capacity, long unloading_speed, ArrayList cargo_names, long hobos, ArrayList ingredients_count, long eating_time, long stealing_time) { + this.dock_capacity = dock_capacity; + this.unloading_speed = unloading_speed; + this.hobos = hobos; + this.ingredients_count = ingredients_count; + this.eating_time = eating_time; + this.stealing_time = stealing_time; + this.cargo_names = cargo_names; + current_ingredients_count = new ArrayList<>(); + for (int i = 0; i < ingredients_count.size(); ++i) { + current_ingredients_count.add(0L); + } + add_ingredient_mutex = new ReentrantLock(); + cargo_names.forEach(x -> { + cargo_count.put(x, 0L); + }); + + } + + public void Unloading (Ship ship) throws InterruptedException { + long capacity = ship.GetCapacity(); + while (ship.GetCapacity() != 0) { + Thread.sleep(1000); + cargo_count.put(ship.GetCargoName(), Math.min(cargo_count.get(ship.GetCargoName()) + Math.min(ship.GetCapacity(), unloading_speed), dock_capacity)); + ship.SetCapacity(Math.max(0, ship.GetCapacity() - unloading_speed)); + } + Controller.GetConsoleLogger().log(Level.INFO, "Unload " + capacity + " " + ship.GetCargoName()); + } + + public void StartStealing () { + for (int i = 0; i < hobos - 2; ++i) { + Thread thread = new Thread(() -> { + try { + Stealing(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + thread.start(); + } + } + + private void Stealing () throws InterruptedException { + while (true) { + Thread.sleep(stealing_time * 1000); + add_ingredient_mutex.lock(); + int index = GetStealIndex(); + if (index == -1) { + continue; + } + Controller.GetConsoleLogger().log(Level.INFO, "Hobo steals " + cargo_names.get(index)); + cargo_count.put(cargo_names.get(index), cargo_count.get(cargo_names.get(index)) - 1); + current_ingredients_count.set(index, current_ingredients_count.get(index) + 1); + if (CanStartEating()) { + ClearCurrentIngredients(); + Controller.GetConsoleLogger().log(Level.INFO, "Hobos start eating"); + Thread.sleep(eating_time * 1000); + Controller.GetConsoleLogger().log(Level.INFO, "Hobos finish eating"); + } + add_ingredient_mutex.unlock(); + } + } + + private int GetStealIndex () { + ArrayList indexes = new ArrayList(); + for (int i = 0; i < cargo_names.size(); ++i) { + if (cargo_count.get(cargo_names.get(i)) > 0) { + indexes.add(i); + } + } + if (indexes.size() == 0) { + return -1; + } + return indexes.get(new Random().nextInt(0, indexes.size())); + } + + private void ClearCurrentIngredients() { + for (int i = 0; i < current_ingredients_count.size(); ++i) { + current_ingredients_count.set(i, current_ingredients_count.get(i) - ingredients_count.get(i)); + } + } + + private boolean CanStartEating () { + for (int i = 0; i < current_ingredients_count.size(); ++i) { + if (current_ingredients_count.get(i) < ingredients_count.get(i)) { + return false; + } + } + return true; + } + + + + + private final long dock_capacity; + private final long unloading_speed; + + private final long hobos; + + private final long eating_time; + + private final long stealing_time; + + private final ArrayList current_ingredients_count; + + private final Lock add_ingredient_mutex; + + private final ArrayList ingredients_count; + + private final ArrayList cargo_names; + + private final ConcurrentHashMap cargo_count = new ConcurrentHashMap<>(); +} diff --git a/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/Main.java b/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/Main.java new file mode 100644 index 0000000..ababf7e --- /dev/null +++ b/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/Main.java @@ -0,0 +1,24 @@ +package by.DaniilDomnin.doks_and_hobos; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Map; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +public class Main { + + public static void main(String[] args) throws IOException, ParseException, InterruptedException { + Object obj = new JSONParser().parse(new FileReader(args[0])); + JSONObject jo = (JSONObject) obj; + ShipGenerator generator = new ShipGenerator((long)jo.get("ship_capacity_max"), (long)jo.get("ship_capacity_min"), (ArrayList) jo.get("cargo_types")); + Docks docks = new Docks((long)jo.get("dock_capacity"), (long)jo.get("unloading_speed"), (ArrayList) jo.get("cargo_types"), (long)jo.get("hobos"), (ArrayList) jo.get("ingredients_count"), (long)jo.get("eating_time"), (long)jo.get("stealing_time")); + Controller c = new Controller(generator, docks, (long)jo.get("generating_time"), (long)jo.get("max_ships")); + } +} \ No newline at end of file diff --git a/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/Ship.java b/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/Ship.java new file mode 100644 index 0000000..5a8b31f --- /dev/null +++ b/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/Ship.java @@ -0,0 +1,23 @@ +package by.DaniilDomnin.doks_and_hobos; + +public class Ship { + Ship(long capacity, String cargo_name) { + this.capacity = capacity; + this.cargo_name= cargo_name; + } + + public String GetCargoName () { + return cargo_name; + } + + public long GetCapacity () { + return capacity; + } + + public void SetCapacity (long capacity) { + this.capacity = capacity; + } + + private long capacity; + private final String cargo_name; +} diff --git a/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/ShipGenerator.java b/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/ShipGenerator.java new file mode 100644 index 0000000..53af099 --- /dev/null +++ b/lab-02/DocksAndHobos/src/by/DaniilDomnin/doks_and_hobos/ShipGenerator.java @@ -0,0 +1,23 @@ +package by.DaniilDomnin.doks_and_hobos; + +import java.util.ArrayList; +import java.util.Random; + +public class ShipGenerator { + + ShipGenerator(long ship_capacity_max, long ship_capacity_min, ArrayList cargo_names) { + this.ship_capacity_max = ship_capacity_max; + this.ship_capacity_min = ship_capacity_min; + this.cargo_names = cargo_names; + } + + public Ship GenerateShip() { + Random ran = new Random(); + return new Ship(ran.nextLong(ship_capacity_min, ship_capacity_max + 1), cargo_names.get(ran.nextInt(0, cargo_names.size()))); + } + + private long ship_capacity_max; + private long ship_capacity_min; + + private ArrayList cargo_names; +} diff --git a/lab-02/README.md b/lab-02/README.md deleted file mode 100644 index 1d490cf..0000000 --- a/lab-02/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Docks & Hobos -В данной задачем вам предстоит поработать с потоками и примитивами синхронизации в Java. - -# Архитектура приложения -В задаче никого шаблона предложенно не будет, архитектура приложения полностью зависит только от вас (архитектура и чистота также будут оцениваться). - -Все переменные должны задаваться в файле *config.json*. Путь до этого файла передается аргументов в вашу программу, при запуске она читаетс все переменные из этого файла и конструирует необходимые сущности. - ->Всю работу ведите в пакете `by.<ваш ник>.docks_and_hobos` - -# Основные действующие лица -- *Генератор кораблей* — Генератор кораблей каждые _generating_time_ секунд производит подходящий к бухте грузовой корабль. -- *Корабль* — Корабль может иметь разную грузоподъемность и один из типов груза. Диапазон грузоподъемноти коробля от _ship_capacity_min_ до _ship_capacity_max_, все типы грузов перечислены в массиве _cargo_types_. -- *Тонель* — Заходя в бухту, корабли попадают в узкий тоннель, вмещающий только _max_ships_ кораблей. Корабли, которые не могут пройти, тонут. Корабли, находящиеся в тоннеле, ожидают вызова от доков. -- *Доки* — Корабли швартуются в доках для разгрузки, которая идет со скоростью _unloading_speed_ единиц товара в секунду. Разгруженные товары хранятся в доках, максимальное количество хранимого товара _dock_capacity_ для каждой еденицы, если товар не помещается на склад его выкидывают. -- *Бродяги* — В доках обитает _hobos > 2_ бродяг. Они вечно голодны и имеют тягу к высокой кухне, то есть питаются исключительно “Омерзительно длинными бутербродами”. В состав порции, достаточной для временного насыщения ватаги кулинаров входит по _X_i_ единиц _i-го_ ингридента, указывается в массиве _ingridients_count_. Бродяги воруют ингредиенты со складов в доках и готовят их над горящей бочкой: двое всегда занимаются готовкой, остальные воруют ингредиенты, при этом время, за которое один вор может украсть и принести единицу товара - _stealing_time_ секунд. Приготовив необходимую порцию еды, бродяги останавливаются на _eating_time_ секунд, чтобы поесть, после чего продолжают привычную рутину, при этом распределение ответственности за готовку и грабеж происходит случайным образом. - -# Полезные ссылки -- С чего стоит начать - https://goo.gl/f1HZxk. -- Атомарные переменные — https://www.baeldung.com/java-atomic-variables -- Thread — https://www.simplilearn.com/tutorials/java-tutorial/thread-in-java -- Рассказ про многопоточность с семинара — https://disk.yandex.ru/i/rPOOINFYFwGmHQ. -- Рассказ про многопоточность в Java с семинара — https://disk.yandex.ru/i/m16sdlCFlhiJCw. - ->Не испольузйте mutex и atomic там где это не требуется! Использование этих примитивов сильно замедляет выполнение программы. - -# ★ Логирование -Добавьте в свое приложение библиотеку для логирования. Библиотека может быть произвольной. Логирование должно работать следющим образом: -- Логируете все что вам кажется интересным. -- Логирование должно производится и в консоль и в файл (тут может быть разный уровень логирования, чтобы не захлмалять консоль). -- Каждый промежуток времени (выберите самостоятельно) файл с логами должен сохраняться и логи должны начать писаться в другой файл. Название файлов должно содержать начало записи логов в этот файл. -- Сохраните любой лог (до 100 строчек) в репозитории. - -## Полезные ссылки -- Обзор на разные библиотеки логирования — https://habr.com/ru/post/247647/ -- Настройка _log4j_ — https://www.codejava.net/coding/how-to-configure-log4j-as-logging-mechanism-in-java - -# ★★ Condition Variable -Condition variable — явялется примитивом синхронизации и используется для построения более специфичных методов синхронизации таких как *Semaphore*, *Barier* и других. - -Разобраться что такое Condition Variable, как в общих чертах с помощью них построить *Semaphore* и *Barier*. Если необходимо добавить в свой код что-то из описанного выше. - -- Лекция в шаде — https://disk.yandex.ru/i/Lc9eYl-rO4Nunw