diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5e2eb7c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+**.idea
+**/out
+**.iml
diff --git a/lab-02/.gitignore b/lab-02/.gitignore
new file mode 100644
index 0000000..df1a13b
--- /dev/null
+++ b/lab-02/.gitignore
@@ -0,0 +1 @@
+/logs
\ No newline at end of file
diff --git a/lab-02/lab-02.iml b/lab-02/lab-02.iml
new file mode 100644
index 0000000..0cbebe4
--- /dev/null
+++ b/lab-02/lab-02.iml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-02/log_example/README.txt b/lab-02/log_example/README.txt
new file mode 100644
index 0000000..611b51d
--- /dev/null
+++ b/lab-02/log_example/README.txt
@@ -0,0 +1,2 @@
+Логи работают странно, в самом начале вставляют какой-то сценарий из буффера.
+Настоящие начинаются со слов "Start of program"
\ No newline at end of file
diff --git a/lab-02/log_example/log-2023-12-10-22-00-n1.log b/lab-02/log_example/log-2023-12-10-22-00-n1.log
new file mode 100644
index 0000000..bce92bb
--- /dev/null
+++ b/lab-02/log_example/log-2023-12-10-22-00-n1.log
@@ -0,0 +1,349 @@
+2023-12-10 22:00:00.956 [Thread-1] INFO ShipGenerator - Ship PCB is on the horizon
+2023-12-10 22:00:00.960 [Thread-10196] INFO Tunnel - Ship PCB entered the tunnel
+2023-12-10 22:00:01.562 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:01.562 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:01.562 [Thread-10196] INFO Ship - Ship PCB exit the tunnel
+2023-12-10 22:00:01.562 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:01.562 [Thread-0] INFO Dock - Ship PCB entered the dock
+2023-12-10 22:00:01.563 [Thread-0] INFO Dock - Ship PCB with SLAVES is unloading...
+2023-12-10 22:00:01.563 [Thread-0] INFO Dock - SLAVES storage is full
+2023-12-10 22:00:01.563 [Thread-0] INFO Dock - Ship PCB leaves the dock
+2023-12-10 22:00:01.563 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:01.563 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:01.563 [Thread-10196] INFO Ship - Ship PCB disappears on the horizon
+2023-12-10 22:00:03.968 [Thread-1] INFO ShipGenerator - Ship PCC is on the horizon
+2023-12-10 22:00:03.969 [Thread-10197] INFO Tunnel - Ship PCC entered the tunnel
+2023-12-10 22:00:04.574 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:04.574 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:04.574 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:04.574 [Thread-0] INFO Dock - Ship PCC entered the dock
+2023-12-10 22:00:04.574 [Thread-0] INFO Dock - Ship PCC with SLAVES is unloading...
+2023-12-10 22:00:04.574 [Thread-0] INFO Dock - SLAVES storage is full
+2023-12-10 22:00:04.574 [Thread-0] INFO Dock - Ship PCC leaves the dock
+2023-12-10 22:00:04.575 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:04.575 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:04.574 [Thread-10197] INFO Ship - Ship PCC exit the tunnel
+2023-12-10 22:00:06.983 [Thread-1] INFO ShipGenerator - Ship PCD is on the horizon
+2023-12-10 22:00:06.984 [Thread-10198] INFO Tunnel - Ship PCD entered the tunnel
+2023-12-10 22:00:07.575 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:07.575 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:07.575 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:07.575 [Thread-0] INFO Dock - Ship PCD entered the dock
+2023-12-10 22:00:07.575 [Thread-0] INFO Dock - Ship PCD with WEAPON is unloading...
+2023-12-10 22:00:07.576 [Thread-0] INFO Dock - WEAPON storage is full
+2023-12-10 22:00:07.576 [Thread-0] INFO Dock - Ship PCD leaves the dock
+2023-12-10 22:00:07.576 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:07.575 [Thread-10198] INFO Ship - Ship PCD exit the tunnel
+2023-12-10 22:00:07.576 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:10.012 [Thread-1] INFO ShipGenerator - Ship PCE is on the horizon
+2023-12-10 22:00:10.066 [Thread-10199] INFO Tunnel - Ship PCE entered the tunnel
+2023-12-10 22:00:10.585 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:10.586 [Thread-10199] INFO Ship - Ship PCE exit the tunnel
+2023-12-10 22:00:10.586 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:10.587 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:10.587 [Thread-0] INFO Dock - Ship PCE entered the dock
+2023-12-10 22:00:10.587 [Thread-0] INFO Dock - Ship PCE with SLAVES is unloading...
+2023-12-10 22:00:10.587 [Thread-0] INFO Dock - SLAVES storage is full
+2023-12-10 22:00:10.587 [Thread-0] INFO Dock - Ship PCE leaves the dock
+2023-12-10 22:00:10.587 [Thread-10199] INFO Ship - Ship PCE disappears on the horizon
+2023-12-10 22:00:10.587 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:10.588 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:13.025 [Thread-1] INFO ShipGenerator - Ship PCF is on the horizon
+2023-12-10 22:00:13.028 [Thread-10200] INFO Tunnel - Ship PCF entered the tunnel
+2023-12-10 22:00:13.594 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:13.594 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:13.594 [Thread-10200] INFO Ship - Ship PCF exit the tunnel
+2023-12-10 22:00:13.594 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:13.595 [Thread-0] INFO Dock - Ship PCF entered the dock
+2023-12-10 22:00:13.595 [Thread-0] INFO Dock - Ship PCF with RAW_MATERIALS is unloading...
+2023-12-10 22:00:13.595 [Thread-0] INFO Dock - RAW_MATERIALS storage is full
+2023-12-10 22:00:13.595 [Thread-0] INFO Dock - Ship PCF leaves the dock
+2023-12-10 22:00:13.595 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:13.595 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:13.595 [Thread-10200] INFO Ship - Ship PCF disappears on the horizon
+2023-12-10 22:00:16.037 [Thread-1] INFO ShipGenerator - Ship PCG is on the horizon
+2023-12-10 22:00:16.039 [Thread-10201] INFO Tunnel - Ship PCG entered the tunnel
+2023-12-10 22:00:16.603 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:16.603 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:16.603 [Thread-10201] INFO Ship - Ship PCG exit the tunnel
+2023-12-10 22:00:16.603 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:16.604 [Thread-0] INFO Dock - Ship PCG entered the dock
+2023-12-10 22:00:16.604 [Thread-0] INFO Dock - Ship PCG with WEAPON is unloading...
+2023-12-10 22:00:16.604 [Thread-0] INFO Dock - WEAPON storage is full
+2023-12-10 22:00:16.604 [Thread-0] INFO Dock - Ship PCG leaves the dock
+2023-12-10 22:00:16.604 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:16.604 [Thread-10201] INFO Ship - Ship PCG disappears on the horizon
+2023-12-10 22:00:16.604 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:19.042 [Thread-1] INFO ShipGenerator - Ship PCH is on the horizon
+2023-12-10 22:00:19.043 [Thread-10202] INFO Tunnel - Ship PCH entered the tunnel
+2023-12-10 22:00:19.607 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:19.610 [Thread-10202] INFO Ship - Ship PCH exit the tunnel
+2023-12-10 22:00:19.611 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:19.612 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:19.613 [Thread-0] INFO Dock - Ship PCH entered the dock
+2023-12-10 22:00:19.613 [Thread-0] INFO Dock - Ship PCH with SLAVES is unloading...
+2023-12-10 22:00:19.613 [Thread-0] INFO Dock - SLAVES storage is full
+2023-12-10 22:00:19.613 [Thread-0] INFO Dock - Ship PCH leaves the dock
+2023-12-10 22:00:19.613 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:19.613 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:19.614 [Thread-10202] INFO Ship - Ship PCH disappears on the horizon
+2023-12-10 22:00:22.043 [Thread-1] INFO ShipGenerator - Ship PCI is on the horizon
+2023-12-10 22:00:22.045 [Thread-10203] INFO Tunnel - Ship PCI entered the tunnel
+2023-12-10 22:00:22.644 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:22.654 [Thread-10203] INFO Ship - Ship PCI exit the tunnel
+2023-12-10 22:00:22.654 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:22.666 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:22.666 [Thread-0] INFO Dock - Ship PCI entered the dock
+2023-12-10 22:00:22.666 [Thread-0] INFO Dock - Ship PCI with FOOD is unloading...
+2023-12-10 22:00:22.667 [Thread-0] INFO Dock - FOOD storage is full
+2023-12-10 22:00:22.667 [Thread-0] INFO Dock - Ship PCI leaves the dock
+2023-12-10 22:00:22.683 [Thread-10203] INFO Ship - Ship PCI disappears on the horizon
+2023-12-10 22:00:22.683 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:22.684 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:25.058 [Thread-1] INFO ShipGenerator - Ship PCJ is on the horizon
+2023-12-10 22:00:25.064 [Thread-10204] INFO Tunnel - Ship PCJ entered the tunnel
+2023-12-10 22:00:25.685 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:25.685 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:25.685 [Thread-10204] INFO Ship - Ship PCJ exit the tunnel
+2023-12-10 22:00:25.685 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:25.686 [Thread-0] INFO Dock - Ship PCJ entered the dock
+2023-12-10 22:00:25.686 [Thread-0] INFO Dock - Ship PCJ with SLAVES is unloading...
+2023-12-10 22:00:25.686 [Thread-0] INFO Dock - SLAVES storage is full
+2023-12-10 22:00:25.686 [Thread-0] INFO Dock - Ship PCJ leaves the dock
+2023-12-10 22:00:25.686 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:25.686 [Thread-10204] INFO Ship - Ship PCJ disappears on the horizon
+2023-12-10 22:00:25.686 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:28.066 [Thread-1] INFO ShipGenerator - Ship PCK is on the horizon
+2023-12-10 22:00:28.068 [Thread-10205] INFO Tunnel - Ship PCK entered the tunnel
+2023-12-10 22:00:28.698 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:28.698 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:28.698 [Thread-10205] INFO Ship - Ship PCK exit the tunnel
+2023-12-10 22:00:28.699 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:28.699 [Thread-0] INFO Dock - Ship PCK entered the dock
+2023-12-10 22:00:28.700 [Thread-0] INFO Dock - Ship PCK with FOOD is unloading...
+2023-12-10 22:00:28.700 [Thread-0] INFO Dock - FOOD storage is full
+2023-12-10 22:00:28.700 [Thread-0] INFO Dock - Ship PCK leaves the dock
+2023-12-10 22:00:28.700 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:28.700 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:28.700 [Thread-10205] INFO Ship - Ship PCK disappears on the horizon
+2023-12-10 22:00:31.070 [Thread-1] INFO ShipGenerator - Ship PCL is on the horizon
+2023-12-10 22:00:31.072 [Thread-10206] INFO Tunnel - Ship PCL entered the tunnel
+2023-12-10 22:00:31.706 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:31.708 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:31.708 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:31.708 [Thread-0] INFO Dock - Ship PCL entered the dock
+2023-12-10 22:00:31.708 [Thread-0] INFO Dock - Ship PCL with FOOD is unloading...
+2023-12-10 22:00:31.724 [Thread-0] INFO Dock - FOOD storage is full
+2023-12-10 22:00:31.725 [Thread-0] INFO Dock - Ship PCL leaves the dock
+2023-12-10 22:00:31.725 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:31.726 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:31.708 [Thread-10206] INFO Ship - Ship PCL exit the tunnel
+2023-12-10 22:00:34.080 [Thread-1] INFO ShipGenerator - Ship PCM is on the horizon
+2023-12-10 22:00:34.081 [Thread-10207] INFO Tunnel - Ship PCM entered the tunnel
+2023-12-10 22:00:34.727 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:34.728 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:34.728 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:34.728 [Thread-10207] INFO Ship - Ship PCM exit the tunnel
+2023-12-10 22:00:34.728 [Thread-0] INFO Dock - Ship PCM entered the dock
+2023-12-10 22:00:34.730 [Thread-0] INFO Dock - Ship PCM with CHEMICAL is unloading...
+2023-12-10 22:00:34.730 [Thread-0] INFO Dock - CHEMICAL storage is full
+2023-12-10 22:00:34.730 [Thread-0] INFO Dock - Ship PCM leaves the dock
+2023-12-10 22:00:34.730 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:34.730 [Thread-10207] INFO Ship - Ship PCM disappears on the horizon
+2023-12-10 22:00:34.730 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:37.095 [Thread-1] INFO ShipGenerator - Ship PCN is on the horizon
+2023-12-10 22:00:37.097 [Thread-10208] INFO Tunnel - Ship PCN entered the tunnel
+2023-12-10 22:00:37.740 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:37.741 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:37.741 [Thread-10208] INFO Ship - Ship PCN exit the tunnel
+2023-12-10 22:00:37.741 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:37.741 [Thread-0] INFO Dock - Ship PCN entered the dock
+2023-12-10 22:00:37.741 [Thread-0] INFO Dock - Ship PCN with RAW_MATERIALS is unloading...
+2023-12-10 22:00:37.741 [Thread-0] INFO Dock - RAW_MATERIALS storage is full
+2023-12-10 22:00:37.742 [Thread-0] INFO Dock - Ship PCN leaves the dock
+2023-12-10 22:00:37.742 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:37.742 [Thread-10208] INFO Ship - Ship PCN disappears on the horizon
+2023-12-10 22:00:37.742 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:40.108 [Thread-1] INFO ShipGenerator - Ship PCO is on the horizon
+2023-12-10 22:00:40.110 [Thread-10209] INFO Tunnel - Ship PCO entered the tunnel
+2023-12-10 22:00:40.743 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:40.743 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:40.743 [Thread-10209] INFO Ship - Ship PCO exit the tunnel
+2023-12-10 22:00:40.744 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:40.744 [Thread-0] INFO Dock - Ship PCO entered the dock
+2023-12-10 22:00:40.744 [Thread-0] INFO Dock - Ship PCO with FOOD is unloading...
+2023-12-10 22:00:40.744 [Thread-0] INFO Dock - FOOD storage is full
+2023-12-10 22:00:40.744 [Thread-0] INFO Dock - Ship PCO leaves the dock
+2023-12-10 22:00:40.744 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:40.744 [Thread-10209] INFO Ship - Ship PCO disappears on the horizon
+2023-12-10 22:00:40.745 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:43.114 [Thread-1] INFO ShipGenerator - Ship PCP is on the horizon
+2023-12-10 22:00:43.116 [Thread-10210] INFO Tunnel - Ship PCP entered the tunnel
+2023-12-10 22:00:43.749 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:43.749 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:43.749 [Thread-10210] INFO Ship - Ship PCP exit the tunnel
+2023-12-10 22:00:43.749 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:43.750 [Thread-0] INFO Dock - Ship PCP entered the dock
+2023-12-10 22:00:43.750 [Thread-0] INFO Dock - Ship PCP with CHEMICAL is unloading...
+2023-12-10 22:00:43.750 [Thread-0] INFO Dock - CHEMICAL storage is full
+2023-12-10 22:00:43.750 [Thread-0] INFO Dock - Ship PCP leaves the dock
+2023-12-10 22:00:43.750 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:43.750 [Thread-10210] INFO Ship - Ship PCP disappears on the horizon
+2023-12-10 22:00:43.750 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:45.512 [main] INFO Main - Start of program.
+2023-12-10 22:00:45.555 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:45.559 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:45.566 [Thread-2] INFO Hobo - This is an ingredients storage:
+2023-12-10 22:00:45.568 [Thread-2] INFO Hobo -
+---------------
+| 0 | 0 | 0 |
+---------------
+2023-12-10 22:00:45.569 [Thread-2] INFO Hobo - It consists of bread, grain, pizza
+2023-12-10 22:00:45.570 [Thread-2] INFO HoboTent - Day 0
+2023-12-10 22:00:45.572 [Thread-2] INFO Hobo - Sviatoslav has materialized from garbage
+2023-12-10 22:00:45.578 [Thread-2] INFO Hobo - Dobrojir has materialized from blood
+2023-12-10 22:00:45.579 [Thread-2] INFO Hobo - Tihomir has materialized from ashes
+2023-12-10 22:00:45.581 [Thread-2] INFO Hobo - Ratibor has materialized from puddle on the asphalt
+2023-12-10 22:00:45.586 [Thread-4] INFO Hobo - Dobrojir steals
+2023-12-10 22:00:45.587 [Thread-4] INFO Hobo -
+---------------
+| 0 | 0 | 0 |
+---------------
+2023-12-10 22:00:45.588 [Thread-5] INFO Hobo - Tihomir cooks
+2023-12-10 22:00:45.586 [Thread-3] INFO Hobo - Sviatoslav steals
+2023-12-10 22:00:45.589 [Thread-5] INFO Hobo - Tihomir is waiting for ingredients...
+2023-12-10 22:00:45.592 [Thread-6] INFO Hobo - Ratibor cooks
+2023-12-10 22:00:46.116 [Thread-1] INFO ShipGenerator - Ship PCQ is on the horizon
+2023-12-10 22:00:46.117 [Thread-10211] INFO Tunnel - Ship PCQ entered the tunnel
+2023-12-10 22:00:46.764 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:46.764 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:46.764 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:46.764 [Thread-10211] INFO Ship - Ship PCQ exit the tunnel
+2023-12-10 22:00:46.764 [Thread-0] INFO Dock - Ship PCQ entered the dock
+2023-12-10 22:00:46.765 [Thread-0] INFO Dock - Ship PCQ with CHEMICAL is unloading...
+2023-12-10 22:00:46.765 [Thread-0] INFO Dock - CHEMICAL storage is full
+2023-12-10 22:00:46.765 [Thread-0] INFO Dock - Ship PCQ leaves the dock
+2023-12-10 22:00:46.765 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:46.765 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:46.765 [Thread-10211] INFO Ship - Ship PCQ disappears on the horizon
+2023-12-10 22:00:47.589 [Thread-4] INFO Hobo - Dobrojir stole bread
+2023-12-10 22:00:47.590 [Thread-4] INFO Hobo -
+---------------
+| 1 | 0 | 0 |
+---------------
+2023-12-10 22:00:47.592 [Thread-3] INFO Hobo - Sviatoslav stole bread
+2023-12-10 22:00:47.592 [Thread-6] INFO Hobo - Ratibor is waiting for ingredients...
+2023-12-10 22:00:48.563 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:48.563 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:48.572 [Thread-1] INFO ShipGenerator - Ship A is on the horizon
+2023-12-10 22:00:48.572 [Thread-7] INFO Tunnel - Ship A entered the tunnel
+2023-12-10 22:00:49.127 [Thread-1] INFO ShipGenerator - Ship PCR is on the horizon
+2023-12-10 22:00:49.128 [Thread-10212] INFO Tunnel - Ship PCR entered the tunnel
+2023-12-10 22:00:49.594 [Thread-6] INFO Hobo - Ratibor is waiting for ingredients...
+2023-12-10 22:00:49.594 [Thread-4] INFO Hobo - Dobrojir stole grain
+2023-12-10 22:00:49.594 [Thread-3] INFO Hobo - Sviatoslav stole grain
+2023-12-10 22:00:49.594 [Thread-4] INFO Hobo -
+---------------
+| 2 | 1 | 0 |
+---------------
+2023-12-10 22:00:49.767 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:49.767 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:49.767 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:49.767 [Thread-0] INFO Dock - Ship PCR entered the dock
+2023-12-10 22:00:49.767 [Thread-0] INFO Dock - Ship PCR with WEAPON is unloading...
+2023-12-10 22:00:49.767 [Thread-0] INFO Dock - WEAPON storage is full
+2023-12-10 22:00:49.767 [Thread-0] INFO Dock - Ship PCR leaves the dock
+2023-12-10 22:00:49.767 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:49.767 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:49.767 [Thread-10212] INFO Ship - Ship PCR exit the tunnel
+2023-12-10 22:00:51.575 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:51.575 [Thread-1] INFO ShipGenerator - Ship B is on the horizon
+2023-12-10 22:00:51.575 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:51.576 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:51.576 [Thread-7] INFO Ship - Ship A exit the tunnel
+2023-12-10 22:00:51.576 [Thread-8] INFO Tunnel - Ship B entered the tunnel
+2023-12-10 22:00:51.576 [Thread-0] INFO Dock - Ship A entered the dock
+2023-12-10 22:00:51.577 [Thread-0] INFO Dock - Ship A with CHEMICAL is unloading...
+2023-12-10 22:00:51.577 [Thread-0] INFO Dock - The ship A is unloading...
+2023-12-10 22:00:51.605 [Thread-4] INFO Hobo - Dobrojir stole pizza
+2023-12-10 22:00:51.605 [Thread-5] INFO Hobo - Tihomir is waiting for ingredients...
+2023-12-10 22:00:51.605 [Thread-3] INFO Hobo - Sviatoslav stole pizza
+2023-12-10 22:00:51.605 [Thread-3] INFO Hobo - Sviatoslav finished working
+2023-12-10 22:00:51.605 [Thread-4] INFO Hobo - Dobrojir finished working
+2023-12-10 22:00:52.137 [Thread-1] INFO ShipGenerator - Ship PCS is on the horizon
+2023-12-10 22:00:52.149 [Thread-10213] INFO Tunnel - Ship PCS entered the tunnel
+2023-12-10 22:00:52.768 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:52.768 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:52.768 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:52.768 [Thread-10213] INFO Ship - Ship PCS exit the tunnel
+2023-12-10 22:00:52.768 [Thread-0] INFO Dock - Ship PCS entered the dock
+2023-12-10 22:00:52.768 [Thread-0] INFO Dock - Ship PCS with CHEMICAL is unloading...
+2023-12-10 22:00:52.769 [Thread-0] INFO Dock - CHEMICAL storage is full
+2023-12-10 22:00:52.769 [Thread-0] INFO Dock - Ship PCS leaves the dock
+2023-12-10 22:00:52.769 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:52.769 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:52.769 [Thread-10213] INFO Ship - Ship PCS disappears on the horizon
+2023-12-10 22:00:53.606 [Thread-5] INFO Hobo - Tihomir finished working
+2023-12-10 22:00:53.606 [Thread-6] INFO Hobo - Ratibor is waiting for ingredients...
+2023-12-10 22:00:54.584 [Thread-0] INFO Dock - The ship A is unloading...
+2023-12-10 22:00:54.584 [Thread-1] INFO ShipGenerator - Ship C is on the horizon
+2023-12-10 22:00:54.585 [Thread-9] INFO Tunnel - Ship C entered the tunnel
+2023-12-10 22:00:55.160 [Thread-1] INFO ShipGenerator - Ship PCT is on the horizon
+2023-12-10 22:00:55.160 [Thread-10214] INFO Tunnel - Ship PCT entered the tunnel
+2023-12-10 22:00:55.615 [Thread-6] INFO Hobo - Ratibor finished working
+2023-12-10 22:00:55.615 [Thread-6] INFO HoboTent - All came from work. The feast has begun. There are 0 sandwiches on the table
+2023-12-10 22:00:55.616 [Thread-2] INFO HoboTent - Ratibor died from hunger
+2023-12-10 22:00:55.616 [Thread-2] INFO HoboTent - Tihomir ate Sviatoslav
+2023-12-10 22:00:55.616 [Thread-2] INFO HoboTent - Dobrojir is hungry
+2023-12-10 22:00:55.616 [Thread-2] INFO HoboTent - The feast is over
+2023-12-10 22:00:55.617 [Thread-2] INFO HoboTent - Day 1
+2023-12-10 22:00:55.617 [Thread-2] INFO Hobo - Dobrinya has materialized from dust
+2023-12-10 22:00:55.617 [Thread-2] INFO Hobo - Tishilo has materialized from blood
+2023-12-10 22:00:55.617 [Thread-5] INFO Hobo - Tihomir cooks
+2023-12-10 22:00:55.617 [Thread-4] INFO Hobo - Dobrojir steals
+2023-12-10 22:00:55.618 [Thread-10] INFO Hobo - Dobrinya steals
+2023-12-10 22:00:55.618 [Thread-5] INFO Hobo - Tihomir is waiting for ingredients...
+2023-12-10 22:00:55.618 [Thread-11] INFO Hobo - Tishilo cooks
+2023-12-10 22:00:55.618 [Thread-10] INFO Hobo -
+---------------
+| 2 | 2 | 2 |
+---------------
+2023-12-10 22:00:55.770 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:55.771 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:55.771 [Thread-10214] INFO Ship - Ship PCT exit the tunnel
+2023-12-10 22:00:55.771 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:55.771 [Thread-0] INFO Dock - Ship PCT entered the dock
+2023-12-10 22:00:55.771 [Thread-0] INFO Dock - Ship PCT with CHEMICAL is unloading...
+2023-12-10 22:00:55.772 [Thread-0] INFO Dock - CHEMICAL storage is full
+2023-12-10 22:00:55.772 [Thread-0] INFO Dock - Ship PCT leaves the dock
+2023-12-10 22:00:55.772 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:55.772 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:55.772 [Thread-10214] INFO Ship - Ship PCT disappears on the horizon
+2023-12-10 22:00:57.586 [Thread-0] INFO Dock - The ship A is unloading...
+2023-12-10 22:00:57.586 [Thread-1] INFO ShipGenerator - Ship D is on the horizon
+2023-12-10 22:00:57.588 [Thread-12] INFO Tunnel - Ship D entered the tunnel
+2023-12-10 22:00:57.619 [Thread-10] INFO Hobo - Dobrinya stole pizza
+2023-12-10 22:00:57.620 [Thread-10] INFO Hobo -
+---------------
+| 2 | 2 | 3 |
+---------------
+2023-12-10 22:00:57.633 [Thread-11] INFO Hobo - Tishilo started cooking!
+2023-12-10 22:00:57.633 [Thread-4] INFO Hobo - Dobrojir stole pizza
+2023-12-10 22:00:58.161 [Thread-1] INFO ShipGenerator - Ship PCU is on the horizon
+2023-12-10 22:00:58.162 [Thread-10215] INFO Tunnel - Ship PCU entered the tunnel
+2023-12-10 22:00:58.775 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:58.775 [Thread-0] INFO Dock - There is a response
+2023-12-10 22:00:58.775 [Thread-0] INFO Dock - Dock ready for ship unloading
+2023-12-10 22:00:58.775 [Thread-0] INFO Dock - Ship PCU entered the dock
+2023-12-10 22:00:58.775 [Thread-0] INFO Dock - Ship PCU with FOOD is unloading...
+2023-12-10 22:00:58.775 [Thread-10215] INFO Ship - Ship PCU exit the tunnel
+2023-12-10 22:00:58.775 [Thread-0] INFO Dock - FOOD storage is full
+2023-12-10 22:00:58.776 [Thread-0] INFO Dock - Ship PCU leaves the dock
+2023-12-10 22:00:58.776 [Thread-0] INFO Dock - Dock calls
+2023-12-10 22:00:58.776 [Thread-0] INFO Dock - There is no response, wait a little...
+2023-12-10 22:00:59.635 [Thread-4] INFO Hobo - Dobrojir stole bread
+2023-12-10 22:00:59.635 [Thread-10] INFO Hobo - Dobrinya stole bread
+2023-12-10 22:00:59.635 [Thread-10] INFO Hobo -
+---------------
+| 2 | 0 | 1 |
+---------------
diff --git a/lab-02/resources/config.json b/lab-02/resources/config.json
new file mode 100644
index 0000000..97afeda
--- /dev/null
+++ b/lab-02/resources/config.json
@@ -0,0 +1,20 @@
+{
+ "generating_time" : 3,
+ "ship_capacity_min" : 10,
+ "ship_capacity_max" : 100,
+ "cargo_types" : [
+ "CHEMICAL",
+ "FOOD",
+ "WEAPON",
+ "RAW_MATERIALS",
+ "SLAVES"
+ ],
+ "max_ships" : 3,
+ "unloading_speed" : 10,
+ "dock_capacity" : 50,
+ "hobos" : 4,
+ "ingredients_count" : [2, 2, 3],
+ "stealing_time" : 2,
+ "crimes_per_day" : 3,
+ "cooking_time": 10
+}
\ No newline at end of file
diff --git a/lab-02/resources/log4j2.xml b/lab-02/resources/log4j2.xml
new file mode 100644
index 0000000..5fcb704
--- /dev/null
+++ b/lab-02/resources/log4j2.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-02/resources/names.txt b/lab-02/resources/names.txt
new file mode 100644
index 0000000..2c9e0dd
--- /dev/null
+++ b/lab-02/resources/names.txt
@@ -0,0 +1,4 @@
+Sviatoslav Dobrojir Tihomir Ratibor Dobrinya Tishilo
+Naum Bogdan Borislav Dobrojir Zvenislav Yaropolk
+Svetozar Mirolyub Bogomil Vsemysl Dobrovlad
+Kazimir Luchezar Milomir Unislav Snovid Jidobor
\ No newline at end of file
diff --git a/lab-02/src/by/waitingsolong/docks_and_hobos/Dock.java b/lab-02/src/by/waitingsolong/docks_and_hobos/Dock.java
new file mode 100644
index 0000000..4fff569
--- /dev/null
+++ b/lab-02/src/by/waitingsolong/docks_and_hobos/Dock.java
@@ -0,0 +1,100 @@
+package by.waitingsolong.docks_and_hobos;
+
+import by.waitingsolong.docks_and_hobos.helpers.CargoType;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class Dock implements Runnable {
+ private static final Logger logger = LogManager.getLogger(Dock.class);
+ private final int unloading_speed;
+ private final int dock_capacity;
+ private volatile Map storage = new ConcurrentHashMap<>();
+ private final Tunnel tunnel;
+ private final long recallDelay;
+ private final Thread thread;
+ public Dock(int unloadingSpeed, int dockCapacity, Tunnel tunnel, long recallDelay) {
+ this.unloading_speed = unloadingSpeed;
+ this.dock_capacity = dockCapacity;
+ this.tunnel = tunnel;
+ this.thread = new Thread(this);
+ this.recallDelay = recallDelay;
+ for (CargoType cargoType : CargoType.values()) {
+ storage.put(cargoType, 0);
+ }
+ }
+
+ @Override
+ public void run() {
+ while (!thread.isInterrupted()) {
+ logger.info("Dock calls");
+ Optional call = tunnel.call();
+ if (call.isPresent()) {
+ logger.info("There is a response");
+ processShip(call.get());
+ } else {
+ logger.info("There is no response, wait a little...");
+ try {
+ Thread.sleep(recallDelay * 1000);
+ } catch (InterruptedException e) {
+ logger.error("Dock is interrupted during sleep", e);
+ return;
+ }
+ }
+ }
+ }
+
+
+ public void processShip(Ship ship) {
+ logger.info("Dock ready for ship unloading");
+ ReentrantLock wakeUpLock = ship.getWakeUpLock();
+ wakeUpLock.lock();
+
+ logger.info("Ship " + ship.getName() + " entered the dock");
+ synchronized (ship) {
+ unload(ship);
+
+ logger.info("Ship " + ship.getName() + " leaves the dock");
+ ship.notify();
+ }
+ }
+
+ private void unload(Ship ship) {
+ CargoType cargoType = ship.getCargoType();
+
+ logger.info("Ship " + ship.getName() + " with " + cargoType.getName() + " is unloading...");
+ while (true) {
+ int unloaded = ship.unload(unloading_speed);
+ if (unloaded == 0) {
+ return;
+ }
+
+ int become = storage.get(cargoType) + unloaded;
+ if (become < dock_capacity) {
+ storage.put(cargoType, become);
+ } else {
+ storage.put(cargoType, dock_capacity);
+ logger.info(cargoType.getName() + " storage is full");
+ return;
+ }
+
+ Thread.yield();
+
+ logger.info("The ship " + ship.getName() + " is unloading...");
+ try {
+ Thread.sleep(recallDelay * 1000);
+ } catch(InterruptedException e) {
+ logger.error("Unloading of ship is interrupted");
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ public void start() {
+ thread.start();
+ }
+}
diff --git a/lab-02/src/by/waitingsolong/docks_and_hobos/Hobo.java b/lab-02/src/by/waitingsolong/docks_and_hobos/Hobo.java
new file mode 100644
index 0000000..435a004
--- /dev/null
+++ b/lab-02/src/by/waitingsolong/docks_and_hobos/Hobo.java
@@ -0,0 +1,205 @@
+package by.waitingsolong.docks_and_hobos;
+
+import by.waitingsolong.docks_and_hobos.helpers.Job;
+import by.waitingsolong.docks_and_hobos.helpers.State;
+import by.waitingsolong.docks_and_hobos.helpers.NameDistributor;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+
+public class Hobo implements Runnable {
+ private static final Logger logger = LogManager.getLogger(Hobo.class);
+ private final String name;
+ private final HoboTent tent;
+ private final Thread thread;
+ private Optional job = Optional.empty();
+ private static final List materializationSources = Arrays.asList("ashes", "sewage", "blood", "dust", "pile of stones", "shipwreck", "garbage", "puddle on the asphalt", "rotten food leftovers");
+ private static final List ingredientNames = Arrays.asList("bread", "grain", "pizza", "sauce", "cheese", "tomatos");
+ private State state;
+ private static Random random = new Random();
+ private boolean reportingThief;
+
+ public Hobo(HoboTent hoboTent) {
+ this.thread = new Thread(this);
+ this.name = NameDistributor.getUniqueName();
+ this.tent = hoboTent;
+ this.state = new State();
+ String materializationSource = materializationSources.get(random.nextInt(materializationSources.size()));
+ logger.info(this.name + " has materialized from " + materializationSource);
+ if (HoboTent.ingredientsStorage.length() > ingredientNames.size()) {
+ throw new RuntimeException("Provide more names to Hobo.ingredientNames or reduce ingredients_count size");
+ }
+ }
+
+ public void setJob(Job job) {
+ this.job = Optional.ofNullable(job);
+ }
+
+ @Override
+ public void run() {
+ while (!thread.isInterrupted()) {
+ try {
+ tent.getSemaphore().acquire();
+ } catch (InterruptedException e) {
+ logger.error(name + " interrupted while acquiring semaphore to start working");
+ Thread.currentThread().interrupt();
+ }
+ tent.getSemaphore().release();
+ if (job.isPresent()) {
+ switch (job.get()) {
+ case Cook -> cook();
+ case Thief -> steal();
+ }
+ }
+ logger.info(name + " finished working");
+ tent.getWorkPhaser().arriveAndAwaitAdvance();
+ eat();
+ tent.getFeastBarrier().await();
+ try {
+ tent.getSemaphore().acquire();
+ tent.getSemaphore().release();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ private void eat() {
+ AtomicInteger atrociouslyLongSandwiches = tent.getAtrociouslyLongSandwiches();
+ if (atrociouslyLongSandwiches.get() > 0) {
+ atrociouslyLongSandwiches.decrementAndGet();
+ state.hunger = false;
+ logger.info(name + " got sandwich");
+ } else {
+ state.hunger = true;
+ }
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ private void cook() {
+ logger.info(name + " cooks");
+ while (tent.getThiefsAreDone().get() > 0) {
+ AtomicIntegerArray storage = HoboTent.ingredientsStorage;
+ boolean enoughIngredients = true;
+
+ synchronized (storage) {
+ for (int i = 0; i < storage.length(); i++) {
+ if (storage.get(i) < tent.getIngredientsCount().get(i)) {
+ enoughIngredients = false;
+ break;
+ }
+ }
+
+ if (enoughIngredients) {
+ logger.info(name + " started cooking!");
+ for (int i = 0; i < storage.length(); i++) {
+ storage.getAndAdd(i, -tent.getIngredientsCount().get(i));
+ }
+ try {
+ Thread.sleep( 1000L * tent.getCookingTime());
+ } catch(InterruptedException e) {
+ logger.error(name + " interrupted while cooking");
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+ tent.getAtrociouslyLongSandwiches().incrementAndGet();
+ logger.info(name + " made a sandwich");
+ } else {
+ logger.info(name + " is waiting for ingredients...");
+ try {
+ Thread.sleep(tent.getStealing_time() * 1000L);
+ } catch(InterruptedException e) {
+ logger.error(name + " interrupted while waiting ingredients");
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
+
+
+ private void steal() {
+ logger.info(name + " steals");
+ int counter = 0;
+ while (counter++ != tent.getCrimes_per_day()) {
+ if (reportingThief) {
+ logIngredientsStorage();
+ }
+
+ AtomicIntegerArray storage = HoboTent.ingredientsStorage;
+ int ingredientIndex = HoboTent.pickDeficitIngredient();
+ try {
+ Thread.sleep(tent.getStealing_time() * 1000L);
+ } catch(InterruptedException e) {
+ logger.error(name + " interrupted while stealing");
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+ logger.info(name + " stole " + ingredientNames.get(ingredientIndex));
+ storage.getAndIncrement(ingredientIndex);
+ }
+ if (reportingThief) reportingThief = false;
+ tent.getThiefsAreDone().decrementAndGet();
+ }
+
+ private static void logIngredientsStorage() {
+ AtomicIntegerArray ingredientsStorage = HoboTent.ingredientsStorage;
+ StringBuilder sb = new StringBuilder();
+ sb.append("\n");
+ for (int i = 0; i < ingredientsStorage.length() * 5; i++) {
+ sb.append("-");
+ }
+ sb.append("\n| ");
+ for (int i = 0; i < ingredientsStorage.length(); i++) {
+ sb.append(ingredientsStorage.get(i));
+ if (i < ingredientsStorage.length() - 1) {
+ sb.append(" | ");
+ }
+ }
+ sb.append(" |\n");
+ for (int i = 0; i < ingredientsStorage.length() * 5; i++) {
+ sb.append("-");
+ }
+ logger.info(sb.toString());
+ }
+
+ static void presentIngredientsStorage() {
+ logger.info("This is an ingredients storage:");
+ logIngredientsStorage();
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < HoboTent.ingredientsStorage.length(); i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(ingredientNames.get(i));
+ }
+ logger.info("It consists of " + sb.toString());
+ }
+
+ void die() {
+ state.dead = true;
+ }
+
+ void interrupt() {
+ thread.interrupt();
+ }
+
+ public void start() {
+ thread.start();
+ }
+
+ public State getState() {
+ return state;
+ }
+
+ public void setReportingThief(boolean reportingThief) {
+ this.reportingThief = reportingThief;
+ }
+}
diff --git a/lab-02/src/by/waitingsolong/docks_and_hobos/HoboTent.java b/lab-02/src/by/waitingsolong/docks_and_hobos/HoboTent.java
new file mode 100644
index 0000000..3b726e4
--- /dev/null
+++ b/lab-02/src/by/waitingsolong/docks_and_hobos/HoboTent.java
@@ -0,0 +1,218 @@
+package by.waitingsolong.docks_and_hobos;
+
+import java.util.*;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+
+import by.waitingsolong.docks_and_hobos.custom.CyclicBarrier;
+import by.waitingsolong.docks_and_hobos.custom.Semaphore;
+import by.waitingsolong.docks_and_hobos.helpers.Job;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+public class HoboTent implements Runnable {
+ private static final Logger logger = LogManager.getLogger(HoboTent.class);
+ private final int hobos;
+ private final int stealing_time;
+ private final int crimes_per_day;
+ private final int cooking_time;
+ private static ArrayList ingredients_count;
+ private final Phaser workPhaser;
+ private Semaphore semaphore;
+ private final CyclicBarrier feastBarrier;
+ private final List hoboList = new ArrayList<>();
+ private final AtomicInteger atrociouslyLongSandwiches = new AtomicInteger(0);
+ public static AtomicIntegerArray ingredientsStorage;
+ private final Thread thread;
+ private AtomicInteger thiefsAreDone;
+ private static Random random = new Random();
+
+ public HoboTent(int hobos, int stealing_time, int crimes_per_day, int cooking_time, ArrayList ingredients_count) {
+ if (hobos <= 2) {
+ throw new IllegalArgumentException("Number of hobos must be greater than 2");
+ }
+ this.semaphore = new Semaphore(1);
+ this.hobos = hobos;
+ this.stealing_time = stealing_time;
+ this.crimes_per_day = crimes_per_day;
+ this.cooking_time = cooking_time;
+ HoboTent.ingredients_count = ingredients_count;
+ this.workPhaser = new Phaser(hobos + 1) {
+ @Override
+ protected boolean onAdvance(int phase, int registeredParties) {
+ logger.info("All came from work. The feast has begun. There are " + atrociouslyLongSandwiches.get() + " sandwiches on the table");
+ return super.onAdvance(phase, registeredParties);
+ }
+ };
+ this.feastBarrier = new CyclicBarrier(hobos + 1);
+ this.thread = new Thread(this);
+ this.thiefsAreDone = new AtomicInteger(0);
+ HoboTent.ingredientsStorage = new AtomicIntegerArray(ingredients_count.size());
+ }
+
+ // semaphore guarantee that HoboTent will be executed first after the feast
+ @Override
+ public void run() {
+ Hobo.presentIngredientsStorage();
+
+ try {
+ semaphore.acquire();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+
+ while (!thread.isInterrupted()) {
+ logger.info("Day " + workPhaser.getPhase());
+ mobilization();
+ Collections.shuffle(hoboList);
+ distributeJobs();
+ semaphore.release();
+ workPhaser.arriveAndAwaitAdvance();
+ try {
+ semaphore.acquire();
+ } catch (InterruptedException e) {
+ logger.info("Nonsense.");
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+ feastBarrier.await();
+ handleStates();
+ logger.info("The feast is over");
+ }
+ }
+
+ private void mobilization() {
+ while (hoboList.size() < hobos) {
+ Hobo hobo = new Hobo(this);
+ hoboList.add(hobo);
+ hobo.start();
+ }
+ }
+
+ private void distributeJobs() {
+ int cooks = 0;
+ int thiefs = 0;
+ for (int i = 0; i < hoboList.size(); i++) {
+ Hobo hobo = hoboList.get(i);
+ if (i < 2) {
+ hobo.setJob(Job.Cook);
+ cooks++;
+ } else if (i == 3){
+ hobo.setJob(Job.Thief);
+ hobo.setReportingThief(true);
+ thiefs++;
+ }
+ else {
+ hobo.setJob(Job.Thief);
+ thiefs++;
+ }
+ }
+ assert(cooks + thiefs == hobos);
+ thiefsAreDone.set(thiefs);
+ }
+
+ void handleStates() {
+ List hobosToRemove = new ArrayList<>();
+ for (Hobo hobo : hoboList) {
+ if (!hobo.getState().dead && hobo.getState().hunger) {
+ double chance = Math.random();
+ if (chance < 0.25) {
+ hobosToRemove.add(hobo);
+ hobo.die();
+ logger.info(hobo.getName() + " died from hunger");
+ } else if (chance < 0.5) {
+ Hobo eatenHobo = getRandomHobo();
+ hobosToRemove.add(eatenHobo);
+ eatenHobo.die();
+ logger.info(hobo.getName() + " ate " + eatenHobo.getName());
+ } else {
+ logger.info(hobo.getName() + " is hungry");
+ }
+ }
+ }
+
+ hoboList.removeAll(hobosToRemove);
+ for (Hobo hobo : hobosToRemove) {
+ hobo.interrupt();
+ }
+ }
+
+ public Phaser getWorkPhaser() {
+ return workPhaser;
+ }
+
+ public ArrayList getIngredientsCount() {
+ return ingredients_count;
+ }
+
+ public int getStealing_time() {
+ return stealing_time;
+ }
+
+ public int getCookingTime() {
+ return cooking_time;
+ }
+
+ public Semaphore getSemaphore() {
+ return semaphore;
+ }
+
+ public void start() {
+ thread.start();
+ }
+
+ public AtomicInteger getAtrociouslyLongSandwiches() {
+ return atrociouslyLongSandwiches;
+ }
+ public synchronized Hobo getRandomHobo() {
+ if (hoboList.isEmpty()) {
+ throw new RuntimeException("No hobos in HoboTent");
+ }
+ return hoboList.get(random.nextInt(hoboList.size()));
+ }
+
+ public CyclicBarrier getFeastBarrier() {
+ return feastBarrier;
+ }
+
+ public int getCrimes_per_day() {
+ return crimes_per_day;
+ }
+
+ public AtomicInteger getThiefsAreDone() {
+ return thiefsAreDone;
+ }
+
+ public static int pickIngredientProportionally() {
+ int totalWeight = 0;
+ for (int i = 0; i < ingredients_count.size(); i++) {
+ totalWeight += ingredients_count.get(i);
+ }
+ int randomNum = random.nextInt(totalWeight);
+ int cumulativeWeight = 0;
+ int ingredientIndex = -1;
+ for (int i = 0; i < ingredients_count.size(); i++) {
+ cumulativeWeight += ingredients_count.get(i);
+ if (randomNum < cumulativeWeight) {
+ ingredientIndex = i;
+ break;
+ }
+ }
+ return ingredientIndex;
+ }
+
+ public static int pickDeficitIngredient() {
+ int minShareIndex = -1;
+ double minShare = Double.MAX_VALUE;
+ for (int i = 0; i < ingredientsStorage.length(); i++) {
+ double currShare = (double) ingredientsStorage.get(i) / ingredients_count.get(i);
+ if (currShare < minShare) {
+ minShare = currShare;
+ minShareIndex = i;
+ }
+ }
+ return minShareIndex;
+ }
+}
diff --git a/lab-02/src/by/waitingsolong/docks_and_hobos/Main.java b/lab-02/src/by/waitingsolong/docks_and_hobos/Main.java
new file mode 100644
index 0000000..4b95a31
--- /dev/null
+++ b/lab-02/src/by/waitingsolong/docks_and_hobos/Main.java
@@ -0,0 +1,78 @@
+package by.waitingsolong.docks_and_hobos;
+
+import by.waitingsolong.docks_and_hobos.helpers.CargoType;
+import by.waitingsolong.docks_and_hobos.helpers.Config;
+import by.waitingsolong.docks_and_hobos.helpers.NameDistributor;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.json.JSONException;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+
+public class Main {
+ private static final Logger logger = LogManager.getLogger(Main.class);
+
+ public static void main(String[] args) {
+ logger.info("Start of program.");
+
+ String configPath;
+ if (args.length == 0) {
+ Path path = Paths.get("resources", "config.json");
+ configPath = path.toString();
+ } else {
+ configPath = args[0];
+ }
+
+ Config config;
+
+ int max_ships;
+ int ship_capacity_min;
+ int ship_capacity_max;
+ int generating_time;
+ int unloading_speed;
+ int dock_capacity;
+ int hobos;
+ int stealing_time;
+ int crimes_per_day;
+ int cooking_time;
+ ArrayList ingredients_count;
+ ArrayList cargo_types;
+
+ try {
+ config = new Config(configPath);
+
+ max_ships = config.get("max_ships");
+ ship_capacity_max = config.get("ship_capacity_max");
+ ship_capacity_min = config.get("ship_capacity_min");
+ generating_time = config.get("generating_time");
+ unloading_speed = config.get("unloading_speed");
+ dock_capacity = config.get("dock_capacity");
+ hobos = config.get("hobos");
+ ingredients_count = config.get("ingredients_count");
+ stealing_time = config.get("stealing_time");
+ crimes_per_day = config.get("crimes_per_day");
+ cooking_time = config.get("cooking_time");
+ cargo_types = config.get("cargo_types");
+
+ } catch (JSONException | IOException e) {
+ logger.error("Error to parse json");
+ throw new RuntimeException("Error to parse json");
+ }
+
+ CargoType.setTypes(cargo_types);
+ NameDistributor.readUniqueNames(Paths.get("resources", "names.txt").toString());
+
+ Tunnel tunnel = new Tunnel(max_ships);
+ Dock dock = new Dock(unloading_speed, dock_capacity, tunnel, generating_time);
+ ShipGenerator shipGenerator = new ShipGenerator(generating_time, ship_capacity_min, ship_capacity_max, tunnel);
+ HoboTent hoboTent = new HoboTent(hobos, stealing_time, crimes_per_day, cooking_time, ingredients_count);
+
+ dock.start();
+ shipGenerator.start();
+ hoboTent.start();
+ }
+
+}
diff --git a/lab-02/src/by/waitingsolong/docks_and_hobos/Ship.java b/lab-02/src/by/waitingsolong/docks_and_hobos/Ship.java
new file mode 100644
index 0000000..5edb082
--- /dev/null
+++ b/lab-02/src/by/waitingsolong/docks_and_hobos/Ship.java
@@ -0,0 +1,86 @@
+package by.waitingsolong.docks_and_hobos;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import by.waitingsolong.docks_and_hobos.helpers.CargoType;
+import by.waitingsolong.docks_and_hobos.helpers.NameDistributor;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class Ship implements Runnable {
+ private int capacity;
+ private final CargoType cargoType;
+ private final String name;
+ private static final Logger logger = LogManager.getLogger(Ship.class);
+ private static final AtomicInteger shipCount = new AtomicInteger(0);
+ private final Tunnel tunnel;
+ private final Thread thread;
+ private final ReentrantLock wakeUpLock;
+
+ public Ship(int capacity, CargoType cargoType, Tunnel tunnel) {
+ this.capacity = capacity;
+ this.cargoType = cargoType;
+ this.name = NameDistributor.getCounterName(shipCount.getAndIncrement());
+ this.tunnel = tunnel;
+ this.thread = new Thread(this);
+ this.wakeUpLock = new ReentrantLock();
+ }
+
+ public void start() {
+ thread.start();
+ }
+
+ @Override
+ public void run() {
+ tunnel.enter(this);
+ wakeUpLock.lock();
+ synchronized (this) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ logger.info("Ship " + name + " has sunk");
+ Thread.currentThread().interrupt();
+ return;
+ }
+ finally {
+ wakeUpLock.unlock();
+ }
+ }
+ logger.info("Ship " + this.name + " exit the tunnel");
+
+ synchronized (this) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ logger.error("Ship " + name + " is interrupted in the dock", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ logger.info("Ship " + name + " vanished into thin air");
+ thread.interrupt();
+ }
+
+ public int unload(int units) {
+ if (capacity < units) {
+ int given = capacity;
+ capacity = 0;
+ return given;
+ }
+ else {
+ capacity -= units;
+ return units;
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public CargoType getCargoType() {
+ return cargoType;
+ }
+
+ public ReentrantLock getWakeUpLock() { return wakeUpLock; }
+}
diff --git a/lab-02/src/by/waitingsolong/docks_and_hobos/ShipGenerator.java b/lab-02/src/by/waitingsolong/docks_and_hobos/ShipGenerator.java
new file mode 100644
index 0000000..4abc159
--- /dev/null
+++ b/lab-02/src/by/waitingsolong/docks_and_hobos/ShipGenerator.java
@@ -0,0 +1,46 @@
+package by.waitingsolong.docks_and_hobos;
+
+import by.waitingsolong.docks_and_hobos.helpers.CargoType;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Random;
+
+public class ShipGenerator implements Runnable {
+ private static final Logger logger = LogManager.getLogger(ShipGenerator.class);
+ private final int generating_time;
+ private final int ship_capacity_min;
+ private final int ship_capacity_max;
+ private final Tunnel tunnel;
+ private final Thread thread;
+ private static final Random random = new Random();
+
+ public ShipGenerator(int generating_time, int ship_capacity_min, int ship_capacity_max, Tunnel tunnel) {
+ this.generating_time = generating_time;
+ this.ship_capacity_min = ship_capacity_min;
+ this.ship_capacity_max = ship_capacity_max;
+ this.thread = new Thread(this);
+ this.tunnel = tunnel;
+ }
+
+ @Override
+ public void run() {
+ while (!thread.isInterrupted()) {
+ try {
+ Thread.sleep(generating_time * 1000);
+ int capacity = ship_capacity_min + random.nextInt(ship_capacity_max - ship_capacity_min + 1);
+ CargoType cargoType = CargoType.getRandom();
+ Ship ship = new Ship(capacity, cargoType, tunnel);
+ logger.info("Ship " + ship.getName() + " is on the horizon");
+ ship.start();
+ } catch (InterruptedException e) {
+ logger.error("Ship generator is interrupted", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ public void start() {
+ thread.start();
+ }
+}
diff --git a/lab-02/src/by/waitingsolong/docks_and_hobos/Tunnel.java b/lab-02/src/by/waitingsolong/docks_and_hobos/Tunnel.java
new file mode 100644
index 0000000..4e0a2d1
--- /dev/null
+++ b/lab-02/src/by/waitingsolong/docks_and_hobos/Tunnel.java
@@ -0,0 +1,38 @@
+package by.waitingsolong.docks_and_hobos;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Optional;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class Tunnel {
+ private static final Logger logger = LogManager.getLogger(Tunnel.class);
+ private final BlockingQueue tunnel;
+
+ public Tunnel(int maxShips) {
+ this.tunnel = new LinkedBlockingQueue<>(maxShips);
+ }
+
+ public void enter(Ship ship) {
+ if (!tunnel.offer(ship)) {
+ logger.info("Ship " + ship.getName() + " has not entered the tunnel");
+ Thread.currentThread().interrupt();
+ } else {
+ logger.info("Ship " + ship.getName() + " entered the tunnel");
+ }
+ }
+
+ public Optional call() {
+ Ship ship = tunnel.poll();
+ if (ship != null) {
+ synchronized (ship) {
+ ship.notify();
+ }
+ return Optional.of(ship);
+ } else {
+ return Optional.empty();
+ }
+ }
+}
diff --git a/lab-02/src/by/waitingsolong/docks_and_hobos/custom/CyclicBarrier.java b/lab-02/src/by/waitingsolong/docks_and_hobos/custom/CyclicBarrier.java
new file mode 100644
index 0000000..6182fce
--- /dev/null
+++ b/lab-02/src/by/waitingsolong/docks_and_hobos/custom/CyclicBarrier.java
@@ -0,0 +1,49 @@
+package by.waitingsolong.docks_and_hobos.custom;
+
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class CyclicBarrier {
+ private final int parties;
+ private static int generation = 0;
+ private int waiting = 0;
+ private final ReentrantLock lock = new ReentrantLock();
+ private final Condition condition = lock.newCondition();
+ private final Runnable barrierAction;
+
+ public CyclicBarrier(int parties) {
+ this.parties = parties;
+ this.barrierAction = null;
+ }
+
+ public CyclicBarrier(int parties, Runnable barrierAction) {
+ this.parties = parties;
+ this.barrierAction = barrierAction;
+ }
+
+ public void await() {
+ lock.lock();
+ try {
+ waiting++;
+ if (waiting == parties) {
+ if (barrierAction != null) {
+ barrierAction.run();
+ }
+ waiting = 0;
+ generation++;
+ condition.signalAll();
+ } else {
+ int currGeneration = generation;
+ while (waiting < parties && generation == currGeneration) {
+ condition.await();
+ }
+ }
+ } catch(InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ } finally {
+ lock.unlock();
+ }
+ }
+}
diff --git a/lab-02/src/by/waitingsolong/docks_and_hobos/custom/Semaphore.java b/lab-02/src/by/waitingsolong/docks_and_hobos/custom/Semaphore.java
new file mode 100644
index 0000000..5a44554
--- /dev/null
+++ b/lab-02/src/by/waitingsolong/docks_and_hobos/custom/Semaphore.java
@@ -0,0 +1,73 @@
+package by.waitingsolong.docks_and_hobos.custom;
+
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class Semaphore {
+ private int permits;
+ private boolean fair;
+ private ReentrantLock lock;
+ private Condition condition;
+
+ public Semaphore(int permits, boolean fair) {
+ this.permits = permits;
+ this.fair = fair;
+ this.lock = new ReentrantLock(fair);
+ this.condition = lock.newCondition();
+ }
+
+ public Semaphore(int permits) {
+ this(permits, false);
+ }
+
+ public void acquire() throws InterruptedException {
+ lock.lock();
+ try {
+ while (permits == 0) {
+ condition.await();
+ }
+ permits--;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public void acquireUninterruptibly() {
+ lock.lock();
+ try {
+ while (permits == 0) {
+ condition.awaitUninterruptibly();
+ }
+ permits--;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public boolean tryAcquire() {
+
+ lock.lock();
+ try {
+ if (permits > 0) {
+ permits--;
+ return true;
+ } else {
+ return false;
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public void release() {
+ lock.lock();
+ try {
+ permits++;
+ condition.signal();
+ } finally {
+ lock.unlock();
+ }
+ }
+}
+
+
diff --git a/lab-02/src/by/waitingsolong/docks_and_hobos/helpers/CargoType.java b/lab-02/src/by/waitingsolong/docks_and_hobos/helpers/CargoType.java
new file mode 100644
index 0000000..8a64b73
--- /dev/null
+++ b/lab-02/src/by/waitingsolong/docks_and_hobos/helpers/CargoType.java
@@ -0,0 +1,54 @@
+package by.waitingsolong.docks_and_hobos.helpers;
+
+import java.util.*;
+
+public class CargoType {
+ private static final Random random = new Random();
+ private static List types;
+
+ public static void setTypes(List types_) {
+ types = types_;
+
+ }
+ private int id;
+
+ public CargoType(int id) {
+ if (id < 0 || id >= types.size()) {
+ throw new IllegalArgumentException("Invalid CargoType id: " + id);
+ }
+ this.id = id;
+ }
+
+ public static List values() {
+ List cargoTypes = new ArrayList<>();
+ for (int i = 0; i < types.size(); i++) {
+ cargoTypes.add(new CargoType(i));
+ }
+ return cargoTypes;
+ }
+
+ public String getName() {
+ return types.get(id);
+ }
+
+ public static CargoType getRandom() {
+ return new CargoType(random.nextInt(types.size()));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ CargoType cargoType = (CargoType) obj;
+ return id == cargoType.id;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+}
diff --git a/lab-02/src/by/waitingsolong/docks_and_hobos/helpers/Config.java b/lab-02/src/by/waitingsolong/docks_and_hobos/helpers/Config.java
new file mode 100644
index 0000000..d5c8c8f
--- /dev/null
+++ b/lab-02/src/by/waitingsolong/docks_and_hobos/helpers/Config.java
@@ -0,0 +1,34 @@
+package by.waitingsolong.docks_and_hobos.helpers;
+
+import org.json.*;
+
+import java.io.IOException;
+import java.nio.file.*;
+import java.util.ArrayList;
+
+public class Config {
+ private JSONObject config;
+
+ public Config(String path) throws IOException, JSONException {
+ String content = new String(Files.readAllBytes(Paths.get(path)));
+ this.config = new JSONObject(content);
+ }
+
+ public T get(String key) throws JSONException {
+ if (!this.config.has(key)) {
+ throw new IllegalArgumentException("No such key in config.json: " + key);
+ }
+ Object value = this.config.get(key);
+ if (value instanceof JSONArray) {
+ JSONArray array = (JSONArray) value;
+ ArrayList