diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..562b66d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +out/ \ No newline at end of file diff --git a/lab-02/gson-2.10.1.jar b/lab-02/gson-2.10.1.jar new file mode 100644 index 0000000..a88c5bd Binary files /dev/null and b/lab-02/gson-2.10.1.jar differ diff --git a/lab-02/src/by/BelArtem/docks_and_hobos/Dock.java b/lab-02/src/by/BelArtem/docks_and_hobos/Dock.java new file mode 100644 index 0000000..1cae8d3 --- /dev/null +++ b/lab-02/src/by/BelArtem/docks_and_hobos/Dock.java @@ -0,0 +1,67 @@ +package by.BelArtem.docks_and_hobos; + +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicIntegerArray; + +public class Dock implements Runnable{ + + private final int unloadingSpeed; + + private final int dockCapacity; + + private final AtomicIntegerArray stock; + + private final TunnelManager tunnelManager; + + private final ArrayList cargo_types; + + public Dock(int unloadingSpeed, int dockCapacity, + TunnelManager tunnelManager, ArrayList cargo_types) { + + this.unloadingSpeed = unloadingSpeed; + this.dockCapacity = dockCapacity; + stock = new AtomicIntegerArray(cargo_types.size()); + this.tunnelManager = tunnelManager; + this.cargo_types = cargo_types; + } + + public AtomicIntegerArray getStock() { + return stock; + } + + @Override + public void run() { + while(true) { + Ship ship = tunnelManager.getFirstShip(); + if (ship == null) { + continue; + } + System.out.println("New ship info: "); + System.out.println("Cargo: " + ship.getCargoType() + + "; capacity: " + ship.getCapacity()); + String cargoType = ship.getCargoType(); + int index = cargo_types.indexOf(cargoType); + while (ship.getCargoLeft() != 0) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + int itemsToPut = Math.min(ship.getCargoLeft(), this.unloadingSpeed); + this.stock.set(index, Math.min(this.dockCapacity, + this.stock.get(index) + itemsToPut)); + ship.unloadShip(itemsToPut); + System.out.println("Ship info: " + ship.getCargoLeft() + " cargo left"); + System.out.println("The dock " + Thread.currentThread().getName() + " info:"); + for (int i = 0; i < cargo_types.size(); ++i) { + System.out.println(this.stock.get(i)); + } + System.out.println("End of info\n"); + } + System.out.println("==========================="); + + } + + } +} diff --git a/lab-02/src/by/BelArtem/docks_and_hobos/DocksManager.java b/lab-02/src/by/BelArtem/docks_and_hobos/DocksManager.java new file mode 100644 index 0000000..901fda0 --- /dev/null +++ b/lab-02/src/by/BelArtem/docks_and_hobos/DocksManager.java @@ -0,0 +1,23 @@ +package by.BelArtem.docks_and_hobos; + +import java.util.ArrayList; +import java.util.List; + +public class DocksManager implements Runnable{ + + private final List dockList; + + public DocksManager(List dockList) { + this.dockList = dockList; + } + + @Override + public void run() { + ArrayList threads = new ArrayList<>(); + int size = dockList.size(); + for (int i = 0; i < size; ++i) { + threads.add(new Thread(dockList.get(i))); + threads.get(i).start(); + } + } +} diff --git a/lab-02/src/by/BelArtem/docks_and_hobos/Hobo.java b/lab-02/src/by/BelArtem/docks_and_hobos/Hobo.java new file mode 100644 index 0000000..ab8acb8 --- /dev/null +++ b/lab-02/src/by/BelArtem/docks_and_hobos/Hobo.java @@ -0,0 +1,98 @@ +package by.BelArtem.docks_and_hobos; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.atomic.AtomicIntegerArray; + +public class Hobo implements Runnable{ + + private final List dockList; + + private final ArrayList indicesOfIngredientsToSteal = new ArrayList<>(); + + private AtomicIntegerArray necessaryIngredients; + + private final int stealingTime; + + private boolean isCooking; + + + public Hobo(List docks, AtomicIntegerArray necessaryIngredients, int stealingTime) { + this.dockList = docks; + this.necessaryIngredients = necessaryIngredients; + this.stealingTime = stealingTime; + this.isCooking = false; + } + + public boolean getCookingState() { + return this.isCooking; + } + + public void setCookingState(boolean newState) { + this.isCooking = newState; + } + + + public void addIngredient(int index) { + indicesOfIngredientsToSteal.add(index); + } + + public void setNecessaryIngredients (AtomicIntegerArray necessaryIngredients) { + this.necessaryIngredients = necessaryIngredients; + } + + @Override + public void run() { + if (!this.isCooking) { + for (int ingredientIndex : indicesOfIngredientsToSteal) { + steelIngredient(ingredientIndex); + } + return; + } + while (!this.areAllIngredientsCollected()) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + System.out.println("Error happened while attempting to sleep"); + throw new RuntimeException(e); + } + } + } + + private void steelIngredient(int index) { + Random random = new Random(); + while (necessaryIngredients.get(index) > 0) { + int randomDockIndex = random.nextInt(dockList.size()); + AtomicIntegerArray currentStock = dockList.get(randomDockIndex).getStock(); + while (necessaryIngredients.get(index) > 0 && currentStock.get(index) > 0){ + int itemsLeft = currentStock.decrementAndGet(index); + if (itemsLeft < 0){ + currentStock.incrementAndGet(index); + break; + } + + try { + Thread.sleep(this.stealingTime * 1000L); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + System.out.println(Thread.currentThread().getName() + " Has stolen a " + index + " item"); + + necessaryIngredients.set(index, Math.max( 0, necessaryIngredients.get(index) - 1)); + } + } + } + + private boolean areAllIngredientsCollected() { + boolean result = true; + int size = necessaryIngredients.length(); + for (int i = 0; i < size; ++i) { + if (necessaryIngredients.get(i) != 0) { + result = false; + break; + } + } + return result; + } +} diff --git a/lab-02/src/by/BelArtem/docks_and_hobos/HobosManager.java b/lab-02/src/by/BelArtem/docks_and_hobos/HobosManager.java new file mode 100644 index 0000000..01e484f --- /dev/null +++ b/lab-02/src/by/BelArtem/docks_and_hobos/HobosManager.java @@ -0,0 +1,113 @@ +package by.BelArtem.docks_and_hobos; + +import java.util.ArrayList; +import java.util.Random; +import java.util.concurrent.atomic.AtomicIntegerArray; + +public class HobosManager implements Runnable{ + + private final ArrayList hobos; + + private final int eatingTime; + + private ArrayList workingHobos; + + private final AtomicIntegerArray ingredients; + + public HobosManager(ArrayList hobos, int eatingTime, + AtomicIntegerArray ingredients) { + this.hobos = hobos; + this.eatingTime = eatingTime; + this.ingredients = ingredients; + } + + @Override + public void run() { + while (true) { + System.out.println("Hobos are starting stealing"); + AtomicIntegerArray necessaryIngredients = new AtomicIntegerArray(ingredients.length()); + for (int i = 0; i < necessaryIngredients.length(); ++i) { + necessaryIngredients.set(i, ingredients.get(i)); + } + for (Hobo hobo: hobos) { + hobo.setNecessaryIngredients(necessaryIngredients); + } + + this.distributeResponsibilities(); + this.createStrategy(); + + ArrayList threads = new ArrayList<>(); + + int size = hobos.size(); + for (int i = 0; i < size; ++i) { + threads.add(new Thread(hobos.get(i))); + threads.get(i).start(); + } + for (int i = 0; i < size; ++i) { + try { + threads.get(i).join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + System.out.println("Hobos are about to eat"); + try { + Thread.sleep(eatingTime * 1000L); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + + private void distributeResponsibilities() { + for (Hobo hobo: this.hobos) { + hobo.setCookingState(false); + } + + workingHobos = new ArrayList<>(hobos); + + Random random = new Random(); + for (int i = 0; i < 2; ++i) { + int randIndex = random.nextInt(workingHobos.size()); + workingHobos.get(randIndex).setCookingState(true); + workingHobos.remove(randIndex); + } + + } + + private void createStrategy() { + int workingHobosNumber = this.workingHobos.size(); + int ingredientsNumber = this.ingredients.length(); + + if (workingHobosNumber > ingredientsNumber) { + int cyclesCount = workingHobosNumber / ingredientsNumber; + for (int i = 0; i < cyclesCount; ++i) { + for (int j = 0; j < ingredientsNumber; ++j) { + this.workingHobos.get(i * ingredientsNumber + j).addIngredient(j); + } + } + + int freeHobosLeft = workingHobosNumber - cyclesCount * ingredientsNumber; + int startIndex = cyclesCount * ingredientsNumber; + for (int i = 0; i < freeHobosLeft; ++i) { + this.workingHobos.get(startIndex + i).addIngredient(i); + } + return; + } + + int cyclesCount = ingredientsNumber / workingHobosNumber; + for (int i = 0; i < cyclesCount; ++i) { + for (int j = 0; j < workingHobosNumber; ++j) { + this.workingHobos.get(j).addIngredient(i * workingHobosNumber + j); + } + } + + int ingredientLeft = ingredientsNumber - cyclesCount * workingHobosNumber; + int startIndex = cyclesCount * workingHobosNumber; + for (int i = 0; i < ingredientLeft; ++i) { + this.workingHobos.get(i).addIngredient(startIndex + i); + } + } +} diff --git a/lab-02/src/by/BelArtem/docks_and_hobos/Main.java b/lab-02/src/by/BelArtem/docks_and_hobos/Main.java new file mode 100644 index 0000000..d19ceee --- /dev/null +++ b/lab-02/src/by/BelArtem/docks_and_hobos/Main.java @@ -0,0 +1,9 @@ +package by.BelArtem.docks_and_hobos; + +public class Main { + + public static void main(String[] args) throws InterruptedException { + Program program = new Program(); + program.start(); + } +} diff --git a/lab-02/src/by/BelArtem/docks_and_hobos/Program.java b/lab-02/src/by/BelArtem/docks_and_hobos/Program.java new file mode 100644 index 0000000..c18ce4d --- /dev/null +++ b/lab-02/src/by/BelArtem/docks_and_hobos/Program.java @@ -0,0 +1,110 @@ +package by.BelArtem.docks_and_hobos; + +import com.google.gson.*; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicIntegerArray; + +public class Program { + private int generating_time; + private int ship_capacity_min; + private int ship_capacity_max; + private ArrayList cargo_types; + private int max_ships; + private int unloading_speed; + private int dock_capacity; + private int hobos; + private ArrayList ingredients_count; + private int stealing_time; + private int eating_time; + private int docks_number; + + public Program() {} + + private void readFromJsonFile() { + try { + String filePath = "src\\by\\BelArtem\\docks_and_hobos\\config.json"; + JsonObject jsonObject = JsonParser.parseReader(new FileReader(filePath)).getAsJsonObject(); + System.out.println("Hi"); + + generating_time = jsonObject.get("generating_time").getAsInt(); + ship_capacity_min = jsonObject.get("ship_capacity_min").getAsInt(); + ship_capacity_max = jsonObject.get("ship_capacity_max").getAsInt(); + + String[] cargoTypes = new Gson().fromJson(jsonObject.get("cargo_types"), String[].class); + cargo_types = new ArrayList<>(List.of(cargoTypes)); + + max_ships = jsonObject.get("max_ships").getAsInt(); + + unloading_speed = jsonObject.get("unloading_speed").getAsInt(); + dock_capacity = jsonObject.get("dock_capacity").getAsInt(); + hobos = jsonObject.get("hobos").getAsInt(); + + Integer[] ingredientsCount = new Gson().fromJson(jsonObject.get("ingredients_count"), Integer[].class); + ingredients_count = new ArrayList<>(List.of(ingredientsCount)); + + stealing_time = jsonObject.get("stealing_time").getAsInt(); + eating_time = jsonObject.get("eating_time").getAsInt(); + + docks_number = jsonObject.get("docks_number").getAsInt(); + } catch (IOException e) { + System.out.println("Something went wrong during reading from .json file"); + e.printStackTrace(); + } + + if (generating_time < 0 || ship_capacity_min < 0 || + ship_capacity_max < ship_capacity_min || cargo_types.isEmpty() || + max_ships < 0 || unloading_speed < 0 || dock_capacity < 0 || + hobos < 3 || ingredients_count.isEmpty() || stealing_time < 0 || + eating_time < 0 || docks_number < 0) { + throw new IllegalArgumentException("Some input data is invalid"); + } + } + + public void start() { + this.readFromJsonFile(); + + ShipGenerator generator = new ShipGenerator(generating_time, ship_capacity_min, + ship_capacity_max, cargo_types); + + Thread generateThread = new Thread(generator, "Generator"); + + Tunnel tunnel = new Tunnel(max_ships); + TunnelManager tunnelManager = new TunnelManager(tunnel, generator); + + Thread tunnelManagerThread = new Thread(tunnelManager); + + List lst = Collections.synchronizedList(new ArrayList<>()); + for (int i = 0; i < docks_number; ++i) { + lst.add(new Dock(unloading_speed, dock_capacity, tunnelManager, cargo_types)); + } + DocksManager docksManager = new DocksManager(lst); + + Thread dockManagerThread = new Thread(docksManager); + + + int size = ingredients_count.size(); + AtomicIntegerArray ingredients = new AtomicIntegerArray(size); + for (int i = 0; i < size; ++i) { + ingredients.set(i, this.ingredients_count.get(i)); + } + + ArrayList hobosList = new ArrayList<>(); + for (int i = 0; i < this.hobos; ++i) { + hobosList.add(new Hobo(lst, ingredients, stealing_time)); + } + + HobosManager hobosManager = new HobosManager(hobosList, eating_time, ingredients); + + Thread hobosManagerThread = new Thread(hobosManager); + + generateThread.start(); + tunnelManagerThread.start(); + dockManagerThread.start(); + hobosManagerThread.start(); + } + +} diff --git a/lab-02/src/by/BelArtem/docks_and_hobos/Ship.java b/lab-02/src/by/BelArtem/docks_and_hobos/Ship.java new file mode 100644 index 0000000..80d862b --- /dev/null +++ b/lab-02/src/by/BelArtem/docks_and_hobos/Ship.java @@ -0,0 +1,30 @@ +package by.BelArtem.docks_and_hobos; + +public class Ship { + private final int capacity; + private int cargoLeft; + private final String cargoType; + + public Ship(int capacity, String cargoType) { + this.capacity = capacity; + this.cargoType = cargoType; + this.cargoLeft = capacity; + } + + public int getCapacity() { + return capacity; + } + + public String getCargoType() { + return cargoType; + } + + public int getCargoLeft() { + return cargoLeft; + } + + public void unloadShip (int num) { + this.cargoLeft -= num; + } + +} diff --git a/lab-02/src/by/BelArtem/docks_and_hobos/ShipGenerator.java b/lab-02/src/by/BelArtem/docks_and_hobos/ShipGenerator.java new file mode 100644 index 0000000..a20610f --- /dev/null +++ b/lab-02/src/by/BelArtem/docks_and_hobos/ShipGenerator.java @@ -0,0 +1,57 @@ +package by.BelArtem.docks_and_hobos; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +public class ShipGenerator implements Runnable{ + + private final int generating_time; + + private final int ship_capacity_min; + + private final int ship_capacity_max; + + private final ArrayList cargo_types; + + private final int size; + + private final List ships; + + public ShipGenerator(int generatingTime, int ship_capacity_min, + int ship_capacity_max, ArrayList cargo_types) { + this.generating_time = generatingTime; + this.ship_capacity_min = ship_capacity_min; + this.ship_capacity_max = ship_capacity_max; + this.cargo_types = cargo_types; + this.size = cargo_types.size(); + + ships = Collections.synchronizedList(new ArrayList<>()); + } + + public List getShips() { + return ships; + } + + + @Override + public void run() { + Random random = new Random(); + while (true) { + try { + Thread.sleep(generating_time * 1000L); + } catch (InterruptedException e) { + System.out.println("During ship generation something went wrong"); + throw new RuntimeException(e); + } + + int cargoIndex = random.nextInt(size); + String cargoName = cargo_types.get(cargoIndex); + int capacity = ship_capacity_min + + random.nextInt(ship_capacity_max - ship_capacity_min + 1); + Ship ship = new Ship(capacity, cargoName); + this.ships.add(ship); + } + } +} diff --git a/lab-02/src/by/BelArtem/docks_and_hobos/Tunnel.java b/lab-02/src/by/BelArtem/docks_and_hobos/Tunnel.java new file mode 100644 index 0000000..05e458c --- /dev/null +++ b/lab-02/src/by/BelArtem/docks_and_hobos/Tunnel.java @@ -0,0 +1,28 @@ +package by.BelArtem.docks_and_hobos; + +public class Tunnel { + private final int maxShips; + + private int shipsInTunnel = 0; + + public Tunnel(int max_ships) { + this.maxShips = max_ships; + } + + public int getMaxShips() { + return maxShips; + } + + public void setShipsInTunnel(int num) { + this.shipsInTunnel = num; + } + + public boolean isFull () { + return shipsInTunnel == maxShips; + } + + + void removeShips(int number) { + shipsInTunnel -= number; + } +} diff --git a/lab-02/src/by/BelArtem/docks_and_hobos/TunnelManager.java b/lab-02/src/by/BelArtem/docks_and_hobos/TunnelManager.java new file mode 100644 index 0000000..3d80941 --- /dev/null +++ b/lab-02/src/by/BelArtem/docks_and_hobos/TunnelManager.java @@ -0,0 +1,61 @@ +package by.BelArtem.docks_and_hobos; + +import java.util.List; + +public class TunnelManager implements Runnable{ + private final Tunnel tunnel; + private final List ships; + + public TunnelManager(Tunnel tunnel, ShipGenerator shipGenerator) { + this.tunnel = tunnel; + this.ships = shipGenerator.getShips(); + } + + @Override + public void run () { + while (true) { + synchronized (ships){ + this.updateTunnel(); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + System.out.println("Error"); + throw new RuntimeException(e); + } + } + } + + public Ship getFirstShip() { + Ship ship; + synchronized (ships) { + this.updateTunnel(); + if (ships.isEmpty()) { + return null; + } + ship = ships.get(0); + ships.remove(0); + this.tunnel.removeShips(1); + } + return ship; + } + + public void updateTunnel () { + if (tunnel.isFull()){ + while (ships.size() > tunnel.getMaxShips()) { + ships.remove(ships.size() - 1); + } + } else { + if (ships.size() <= tunnel.getMaxShips()){ + tunnel.setShipsInTunnel(ships.size()); + } else { + tunnel.setShipsInTunnel(tunnel.getMaxShips()); + while (ships.size() > tunnel.getMaxShips()) { + ships.remove(ships.size() - 1); + } + } + + } + } +} diff --git a/lab-02/src/by/BelArtem/docks_and_hobos/config.json b/lab-02/src/by/BelArtem/docks_and_hobos/config.json new file mode 100644 index 0000000..17a9586 --- /dev/null +++ b/lab-02/src/by/BelArtem/docks_and_hobos/config.json @@ -0,0 +1,14 @@ +{ + "generating_time": 1, + "ship_capacity_min": 1, + "ship_capacity_max": 10, + "cargo_types": ["Cars", "Groceries", "Resources"], + "max_ships": 5, + "unloading_speed": 3, + "dock_capacity": 10, + "hobos": 5, + "ingredients_count": [2, 2, 3], + "stealing_time": 1, + "eating_time": 3, + "docks_number": 1 +} \ No newline at end of file