【问题标题】:Simulating Multple Entries and Exits in a Vehicle Car Park in Java with Multi Threading使用多线程在 Java 中模拟停车场中的多个入口和出口
【发布时间】:2026-02-01 08:15:01
【问题描述】:

我目前创建了 CarPark 和 Floor 等类来表示停车场。我使用了 Floor 类的 ArrayList 来显示停车场的多个楼层。

我想在不同楼层有多个出入口,配备电梯从一层到另一层。

我应该如何处理这个问题?我希望能够在车辆同时进入和离开时向停车场添加和删除多辆车辆。

在这种情况下如何使用线程和锁?

==============结构============

public class CarPark{

private ArrayList<Floor> floorList;
private ArrayList<Vehicle> vehicleList;

}

实现的方法:

  • getFloorList() : ArrayList
  • getVehicleList(): ArrayList
  • getMostSuitableFloorForVehicle(Vehicle): 楼层
  • addVehicle(Vehicle): 无效

  • getFreeSlots(): 双倍

  • deleteVehicle(String): 车辆

  • getVehiclePercentages(): HashMap

  • getOldestVehicle(): 车辆

  • getLatestVehicle(): 车辆

  • getVehicleById(String): 车辆

  • getVehicleByDayYear(String, String): 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(): 双倍
  • getVehicleList(): ArrayList
  • getPreferredVehicleType(): ArrayList
  • getPossibleVehicleType(): ArrayList
  • getAvailableNumberOfSlots(): 双倍
  • isParkingSlotsSufficient(Vehicle): 布尔值
  • addVehicle(Vehicle 车辆): void
  • getVehicleById(String): 车辆
  • deleteVehicleByInstance(Vehicle): 车辆
  • deleteVehicleByPlateId(String): 车辆
  • toString(): 字符串
  • compareTo(Floor): int

其余只是将添加到停车场的车辆类别。

【问题讨论】:

  • 我添加了一个概述,代码很长
  • 请注意,Stack Overflow 是一个 Q/A 平台,您可以在其中展示具体代码并描述您尝试过的内容以及实际结果与您的预期有何不同,或者提出其他具体问题。它不是一个让其他人免费为你完成整个工作或家庭作业的平台。

标签: java multithreading concurrency simulation


【解决方案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() 从队列(或多个队列)中拉出并推送到随机楼层。

【讨论】:

  • 哇!这是一个好方法......我只有一件小事要问,之后这将是完美的答案。现在假设我们在停车场有 6 个入口和 6 个出口。我是否应该使用线程池并运行模拟随机条目() 6 次来模拟多个条目。并使用停车场中的同步列表来跟踪车辆 ID,然后还运行 6 次模拟随机退出()来模拟系统中的 6 个出口?我也会尝试使用非常实用的队列。
  • 另外只需确保一件事,使每个方法同步会在这里提供任何优势吗?在我修改同步列表的任何地方都需要使用它吗?
  • 查看我的编辑:我会为每个任务使用一个线程:一个用于将车辆推入入口队列,一个用于将车辆推到地板上,一个用于将车辆推到随机出口。跨度>
  • 旁注:您打算可视化停车场模拟吗?如果是,您打算使用哪些工具(swing?JavaFX?)
  • 您使用了同步列表,但您忘记了同步()它们。否则它们不是线程安全的(除非这是问题的意图)。
【解决方案2】:

任何模拟或游戏的基础都是game loop。这是一个循环运行一段时间。

在 Java 中,您可以使用添加到 ThreadRunnable 来运行模拟循环。

这是一个例子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();
            }
        }
    }
    
}

要开始模拟,请使用Runnable 创建一个Thread

new Thread(new SimulationRunnable(view, model, 12)).start();

假设您要模拟汽车进出停车场的 12 小时时段。一种可能的模拟可能是:

  • 前两个小时有汽车进入停车场。
  • 汽车在接下来的八小时内进出停车场。
  • 汽车在过去两个小时内离开停车场。

我们还假设模拟计时器以一分钟代表一小时运行。所以模拟将运行 12 分钟。

我们还假设模拟循环每秒运行五次。这意味着模拟循环将每 200 毫秒执行一次,总共执行 3,600 次。

现在您所要做的就是确定每小时有多少汽车进入停车场,以及每小时有多少汽车离开停车场。价格应该非常接近。

每小时进入停车场的汽车太多会填满停车场。

每小时进入停车场的车辆太少,停车场大部分都是空的。

游戏循环将更新您的模拟模型。每隔一段时间,比如说你在每个游戏小时(分钟)报告你的停车场的状态。

【讨论】:

  • 那么我怎样才能一次将多辆车添加到停车场。我不应该选择一种使用并发循环的方法吗?如果我想将多个项目添加到多个数组列表中,如何在这里实现?
  • @L_Jay:“更新模拟模型”注释是您添加和删除车辆的地方。我建议随机添加和移除车辆,因此添加车辆之间的时间不是恒定的,但您仍然可以预测一段时间内添加的车辆数量。一个主游戏循环可以避免很多并发问题。
  • 当我启动线程时,这是否会一次添加多辆汽车,因为这是一个正常的 for 循环?另外在添加多辆汽车时,我应该在添加车辆时使用锁来锁定地板对象吗?
  • @L_Jay:让我们算一下。您尝试每 200 毫秒创建 5 辆汽车。但是,由于Random,您只有 10%(十分之一)的机会制造汽车。因此,平均而言,您每 200 毫秒制造 1/2 辆汽车,或每秒(分钟)制造 2 1/2 辆汽车。相同的数学运算适用于 remove car 循环。您可以调整循环限制和/或创建/移除汽车的概率。对于汽车创建循环和汽车移除循环,我会保持数学相同。代码中的任何地方都不需要锁。
  • 我明白了,这完全有道理。但我想使用线程和并发,这是我的主要意图。我想要一些指示,请......