#java #multithreading #concurrency #simulation
#java #многопоточность #параллелизм #Симуляция
Вопрос:
В настоящее время я создал такие классы, как автостоянка и этаж, для представления автостоянки. Я использовал ArrayList класса Floor для отображения нескольких уровней на автостоянке.
Я хочу иметь несколько входов и выходов на разных этажах, оборудованных лифтами для перехода с этажа на этаж.
Как мне подойти к этому? Я хочу иметь возможность добавлять и удалять несколько транспортных средств на автостоянку, когда транспортное средство въезжает и выезжает одновременно.
Как я могу использовать потоки и блокировки в этом случае?
============== Структура ============
public class CarPark{
private ArrayList<Floor> floorList;
private ArrayList<Vehicle> vehicleList;
}
Реализованные методы:
- getFloorList() : ArrayList
- getVehicleList(): ArrayList
- getMostSuitableFloorForVehicle(транспортное средство): Этаж
- addVehicle (транспортное средство): void
- getFreeSlots(): double
- deleteVehicle(строка): транспортное средство
- getVehiclePercentages(): HashMap<строка, двойная>
- getOldestVehicle(): транспортное средство
- getLatestVehicle(): транспортное средство
- getVehicleById(строка): транспортное средство
- getVehicleByDayYear(строка, строка): ArrayList
public class Floor implements Comparable<Floor>{ private double maxCapacity; private double currentCapacity; private int currentNumberOfVehicles; private ArrayList<Vehicle> vehicleList; private ArrayList<VehicleTypes> preferredVehicleType; private ArrayList<VehicleTypes> possibleVehicleType;
}
Реализованные методы:
- getCurrentNumberOfVehicles(): int
- getCurrentCapacity(): double
- getVehicleList(): ArrayList
- getPreferredVehicleType(): ArrayList
- getPossibleVehicleType(): ArrayList
- getAvailableNumberOfSlots(): double
- isParkingSlotsSufficient(транспортное средство): логическое значение
- addVehicle (транспортное средство): пустота
- getVehicleById(строка): транспортное средство
- deleteVehicleByInstance(Vehicle): транспортное средство
- deleteVehicleByPlateId(строка): транспортное средство
- toString(): Строка
- Сравнение (этаж): int
Остальные — это просто классы транспортных средств, которые будут добавлены на парковку.
Комментарии:
1. Я добавил обзор, код просто длинный, чтобы поместить
2. Пожалуйста, обратите внимание, что Stack Overflow — это платформа вопросов и ответов, где вы показываете свой конкретный код и описываете, что вы пробовали и как фактический результат отличается от ваших ожиданий, или задаете другие конкретные вопросы. Это не платформа, где другие бесплатно выполняют всю вашу работу или домашнее задание за вас.
Ответ №1:
Следующий mre использует два потока для имитации въезда и выезда автомобилей. Вы можете управлять скоростью, изменяя SLEEP
время:
import java.util.*;
public class CarPark {
private static final int NUM_OF_FLOOORS = 4, SLEEP = 3000;
private final List<Floor> floorList;
public CarPark() {
floorList = Collections.synchronizedList(new ArrayList<>());
for(int i=0; i < NUM_OF_FLOOORS; i ){
floorList.add(new Floor(i));
}
}
private Floor getRandomFloor() {
Collections.shuffle(floorList);
return floorList.get(0);
}
void simulateRandomTraffic(){
simulateRandomEntries();
simulateRandomExits();
}
void simulateRandomEntries(){
new Thread(()->{
while(true){
getRandomFloor().addVehicle(new Vehicle());
try {
Thread.sleep(SLEEP);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}).start();
}
void simulateRandomExits(){
new Thread(()->{
while(true){
getRandomFloor().removeRandomVehicle();
try {
Thread.sleep(SLEEP);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}).start();
}
public static void main(String[] args) {
new CarPark().simulateRandomTraffic();
}
}
class Floor{
private final double maxCapacity = 100;
private final List<Vehicle> vehicleList;
private final int floorNumber;
public Floor(int floorNumber) {
this.floorNumber = floorNumber;
vehicleList = Collections.synchronizedList(new ArrayList<>());
}
public int getCurrentNumberOfVehicles() {
return vehicleList.size();
}
public boolean isFull(){
return maxCapacity <= getCurrentNumberOfVehicles();
}
public boolean isEmpty(){
return getCurrentNumberOfVehicles() <= 0;
}
public int getFloorNumber() {
return floorNumber;
}
private Vehicle getRandomVehicle() {
Collections.shuffle(vehicleList);
return vehicleList.get(0);
}
public boolean removeRandomVehicle(){
if(isEmpty()) {
System.err.println("Floor " getFloorNumber() " is empty. Can't remove vehicle");
return false;
}
return removeVehicle(getRandomVehicle());
}
public boolean addVehicle(Vehicle v){
if(isFull()) {
System.err.println("Floor " getFloorNumber() " is full. Can't add vehicle");
return false;
}
vehicleList.add(v);
System.out.println("Floor " getFloorNumber() " vehicle added (" getCurrentNumberOfVehicles() "/" maxCapacity ")");
return true;
}
public boolean removeVehicle(Vehicle v){
if(isEmpty()) {
System.err.println("Floor " getFloorNumber() " is empty. Can't remove vehicle");
return false;
}
vehicleList.remove(v);
System.out.println("Floor " getFloorNumber() " vehicle removed (" getCurrentNumberOfVehicles() "/" maxCapacity ")");
return true;
}
}
class Vehicle{}
Следующее усовершенствование — автостоянка Queue
.
Попросите третий поток вставить автомобили в Queue
(или несколько запросов, по одному для каждого входа, все заполненные одним и тем же потоком).
simulateRandomEntries()
Извлеките из очереди (или очередей) и переместите на случайный этаж.
Комментарии:
1. Вау! это хороший подход… У меня есть только один маленький вопрос, после которого это будет идеальный ответ. Теперь предположим, например, что у нас есть 6 входов и 6 выходов на автостоянке. Должен ли я использовать пул потоков и запускать simulateRandomEntries() 6 раз, чтобы имитировать множественные записи. И использовать синхронизированный список на автостоянке для отслеживания идентификаторов транспортных средств, а затем также запускать simulateRandomExits() 6 раз, чтобы имитировать 6 выходов в системе? Я также постараюсь использовать очередь, которая очень практична.
2. Кроме того, просто убедитесь в еще одной вещи: обеспечит ли синхронизация каждого метода какое-либо преимущество здесь? Необходимо ли использовать это везде, где я изменяю синхронизированный список?
3. Смотрите мое редактирование: я бы использовал один поток для каждой задачи: один для выталкивания транспортных средств во входные очереди, один для выталкивания транспортных средств на этажи и один для выталкивания транспортных средств к случайному выходу.
4. Примечание: планируете ли вы визуализировать симуляцию парковки? Если да, то какие инструменты вы планируете использовать (swing? JavaFX?)
5. Вы использовали синхронизированный список, но забыли синхронизировать () их. В противном случае они не являются потокобезопасными (если только это не является целью вопроса).
Ответ №2:
Основой любой симуляции или игры является игровой цикл. Это цикл, который выполняется в течение определенного периода времени.
В Java вы можете использовать Runnable, добавленный в поток, для запуска цикла моделирования.
Вот пример Runnable
.
public class SimulationRunnable implements Runnable {
/** Simulation run time, in minutes */
private final int duration;
private final Random random;
private final SimulationView view;
private final SimulationModel model;
public SimulationRunnable(SimulationView view, SimulationModel model,
int duration) {
this.view = view;
this.model = model;
this.duration = duration;
this.random = new Random();
}
@Override
public void run() {
int iterations = duration * 60 * 5;
for (int index = 0; index < iterations; index ) {
// Update simulation model
// We will add cars for the first 10 hours (minutes)
if (index < iterations - 600) {
for (int jndex = 0; jndex < 5; jndex ) {
if (random.nextInt(10) == 5) {
// Add car
}
}
}
// We will remove cars for the last 10 hours (minutee)
if (index > 600) {
for (int jndex = 0; jndex < 5; jndex ) {
if (random.nextInt(10) == 3) {
// Remove car
}
}
}
// Report status every 300 iterations
if (index % 300 == 0) {
}
// Pause simulation loop
try {
Thread.sleep(200L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Чтобы начать симуляцию, вы создаете с Thread
помощью Runnable
.
new Thread(new SimulationRunnable(view, model, 12)).start();
Предположим, вы хотите смоделировать 12-часовой период, в течение которого автомобили въезжают на парковку и выезжают с нее. Одним из возможных вариантов моделирования может быть:
- Автомобили въезжают на парковку в течение первых двух часов.
- Автомобили въезжают и выезжают с парковки в течение следующих восьми часов.
- Автомобили покидают парковку в течение последних двух часов.
Давайте также предположим, что таймер моделирования работает как одна минута, представляющая один час. Таким образом, моделирование будет выполняться в течение 12 минут.
Давайте также предположим, что цикл моделирования выполняется пять раз в секунду. Это означает, что цикл моделирования будет выполняться один раз каждые 200 миллисекунд, в общей сложности 3600 исполнений.
Теперь все, что вам нужно сделать, это решить, сколько автомобилей въезжает на парковку в час и сколько автомобилей покидает парковку в час. Скорости должны быть довольно близки друг к другу.
Слишком много автомобилей, въезжающих на парковку в час, заполнят стоянку.
Слишком мало автомобилей, въезжающих на парковку в час, оставят парковку в основном пустой.
Игровой цикл обновит вашу имитационную модель. Время от времени, скажем, каждый игровой час (минуту), вы сообщаете о состоянии своей парковки.
Комментарии:
1. Итак, как я могу добавить несколько транспортных средств одновременно на парковку. Разве я не должен выбирать подход, который использует для этого параллельный цикл? Если я хочу добавить несколько элементов в несколько списков массивов, как это можно реализовать здесь?
2. @L_Jay: комментарий «Обновить имитационную модель» — это то, где вы добавляете транспортные средства, а также удаляете транспортные средства. Я бы предложил добавлять и удалять транспортные средства случайным образом, чтобы время между добавлением транспортных средств не было постоянным, но вы все равно можете предсказать количество транспортных средств, добавленных за определенный период времени. Вы избегаете множества проблем с параллелизмом с помощью одного основного игрового цикла.
3. Когда я запускаю поток, добавляет ли это сразу несколько автомобилей, поскольку это обычный цикл for ? Также при добавлении нескольких автомобилей следует ли использовать блокировку для блокировки объекта floor при добавлении транспортного средства?
4. @L_Jay: Давайте посчитаем. Вы пытаетесь создать 5 автомобилей каждые 200 миллисекунд. Однако из
Random
-за этого у вас есть только 10% (1 к 10) шанс создания автомобиля. Таким образом, в среднем вы создаете 1/2 автомобиля каждые 200 миллисекунд, или 2 1/2 автомобиля в секунду (минуту). Та же математика применима к циклу удаления автомобиля. Вы можете настроить пределы цикла и / или вероятность создания / удаления автомобиля. Я бы оставил математику одинаковой как для цикла создания автомобиля, так и для цикла удаления автомобиля. В вашем коде не требуется никаких блокировок.5. Я понимаю математику, это имеет смысл. Но я хочу использовать многопоточность и параллелизм, это мое главное намерение здесь. Мне нужны некоторые указания на это, пожалуйста….