java多线程之铁路售票系统

yizhihongxing

Java多线程之铁路售票系统攻略

一、需求分析

铁路售票系统需要满足的主要需求:

  • 售票窗口可以同时售卖多张票,需要支持并发访问。
  • 售票系统需要保证售卖的票数不能超过存库量。
  • 当售票系统资源被其他线程占用时,需要等待其他线程执行完毕后才能获取资源。

二、设计思路

根据需求,我们可以采用以下设计思路:

  1. 定义 Ticket 类表示火车票,其中包括车次、出发时间、座位号等信息。
  2. 定义 TicketDatabase 类表示火车票库存,其中包括每种车次的剩余票数、已售票数等信息。
  3. 定义两个线程类:SellerThread 类表示售票窗口的线程,它们可以并发访问售票系统;CheckerThread 类表示检票员线程,用于检票。
  4. 在 SellerThread 线程中实现售票逻辑,包括从 TicketDatabase 中获取库存、判断是否售出已售光、实现售票等操作。
  5. 在 CheckerThread 线程中实现检票逻辑,包括检票、验证车票有效性等操作。

三、代码实现

1. Ticket 类

public class Ticket {
    private String trainNo; // 车次
    private String departureTime; // 出发时间
    private String seatNo; // 座位号

    public Ticket(String trainNo, String departureTime, String seatNo) {
        this.trainNo = trainNo;
        this.departureTime = departureTime;
        this.seatNo = seatNo;
    }

    public String getTrainNo() {
        return trainNo;
    }

    public String getDepartureTime() {
        return departureTime;
    }

    public String getSeatNo() {
        return seatNo;
    }
}

2. TicketDatabase 类

public class TicketDatabase {
    private final Map<String, Integer> ticketStock = new ConcurrentHashMap<>();

    public TicketDatabase(List<String> trains) {
        for (String train : trains) {
            ticketStock.put(train, 100); // 初始库存为 100 张
        }
    }

    public boolean bookTicket(String trainNo) {
        int stock = getStock(trainNo);
        if (stock > 0) {
            ticketStock.put(trainNo, stock - 1);
            return true;
        }
        return false;
    }

    public int getStock(String trainNo) {
        return ticketStock.getOrDefault(trainNo, 0);
    }
}

3. SellerThread 类

public class SellerThread extends Thread {
    private final TicketDatabase ticketDatabase;

    public SellerThread(TicketDatabase ticketDatabase) {
        this.ticketDatabase = ticketDatabase;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (ticketDatabase) {
                if (ticketDatabase.getStock("G1001") == 0) {
                    break; // 如果该车次已售光,则结束售票
                }
                if (ticketDatabase.bookTicket("G1001")) {
                    System.out.println(Thread.currentThread().getName() + ": " + "售票成功!");
                } else {
                    System.out.println(Thread.currentThread().getName() + ": " + "售票失败!");
                }
            }
        }
    }
}

4. CheckerThread 类

public class CheckerThread extends Thread {
    private final TicketDatabase ticketDatabase;

    public CheckerThread(TicketDatabase ticketDatabase) {
        this.ticketDatabase = ticketDatabase;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(50); // 等待 50ms
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (ticketDatabase) {
                System.out.println("检票员检票: " + ticketDatabase.getStock("G1001"));
            }
        }
    }
}

5. 示例说明

以下是两条示例说明:

示例一

我们创建两个售票窗口线程和一个检票员线程,同时设置库存为 101 张。预期输出结果如下:

售票窗口1: 售票成功!
售票窗口2: 售票成功!
售票窗口1: 售票成功!
售票窗口2: 售票成功!
售票窗口1: 售票成功!
售票窗口2: 售票成功!
售票窗口1: 售票成功!
售票窗口2: 售票成功!
售票窗口1: 售票成功!
售票窗口2: 售票成功!
检票员检票: 91
检票员检票: 89
检票员检票: 87
检票员检票: 85
检票员检票: 83
检票员检票: 81
检票员检票: 79
检票员检票: 77
检票员检票: 75
检票员检票: 73
检票员检票: 71
检票员检票: 69
检票员检票: 67
检票员检票: 65
检票员检票: 63
检票员检票: 61
检票员检票: 59
检票员检票: 57
检票员检票: 55
检票员检票: 53
检票员检票: 51
检票员检票: 49
检票员检票: 47
检票员检票: 45
检票员检票: 43
检票员检票: 41
检票员检票: 39
检票员检票: 37
检票员检票: 35
检票员检票: 33
检票员检票: 31
检票员检票: 29
检票员检票: 27
检票员检票: 25
检票员检票: 23
检票员检票: 21
检票员检票: 19
检票员检票: 17
检票员检票: 15
检票员检票: 13
检票员检票: 11
检票员检票: 9
检票员检票: 7
检票员检票: 5
检票员检票: 3
检票员检票: 1

代码如下:

public static void main(String[] args) {
    List<String> trains = Arrays.asList("G1001");
    TicketDatabase ticketDatabase = new TicketDatabase(trains);
    SellerThread seller1= new SellerThread(ticketDatabase);
    SellerThread seller2 = new SellerThread(ticketDatabase);
    CheckerThread checker = new CheckerThread(ticketDatabase);
    seller1.start();
    seller2.start();
    checker.start();
}

示例二

我们创建三个售票窗口线程和一个检票员线程,同时设置库存为 2 张。预期输出结果如下:

售票窗口2: 售票成功!
售票窗口1: 售票成功!
售票窗口3: 售票失败!
售票窗口3: 售票失败!
检票员检票: 0

代码如下:

public static void main(String[] args) {
    List<String> trains = Arrays.asList("G1001");
    TicketDatabase ticketDatabase = new TicketDatabase(trains);
    SellerThread seller1= new SellerThread(ticketDatabase);
    SellerThread seller2 = new SellerThread(ticketDatabase);
    SellerThread seller3 = new SellerThread(ticketDatabase);
    CheckerThread checker = new CheckerThread(ticketDatabase);
    seller1.start();
    seller2.start();
    seller3.start();
    checker.start();
}

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java多线程之铁路售票系统 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • Java语法基础之函数的使用说明

    Java语法基础之函数的使用说明 在Java中,函数是一个非常重要且常用的机制,我们可以使用它来封装代码,实现模块化以及实现代码的复用,本文将详细讲解Java函数的使用说明,包括函数的定义、调用、参数和返回值等内容。 函数的定义 在Java中,函数的定义包括函数名、参数列表和函数体,它的基本语法如下: [修饰符] 返回类型 函数名(参数列表) { 函数体 }…

    Java 2023年5月23日
    00
  • Python操作多维数组输出和矩阵运算示例

    Python是一门功能强大的编程语言,拥有许多强大的数学运算工具,其中最重要的便是多维数组和矩阵运算。这篇攻略将会讲解如何在Python中进行多维数组输出和矩阵运算,并提供两个示例说明。 多维数组输出 首先,让我们来看一下多维数组输出的方法。在Python中,可以使用numpy库来创建和操作多维数组。下面是一个简单的例子,展示了如何创建一个二维数组,并将其打…

    Java 2023年5月26日
    00
  • Java8中使用流方式查询数据库的方法

    使用流方式查询数据库是Java8中比较常用的操作。以下是一个完整的攻略: 步骤1:引入依赖 在项目的pom.xml文件中添加以下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter…

    Java 2023年5月20日
    00
  • CentOS安装solr 4.10.3详细教程

    CentOS安装solr 4.10.3详细教程 简介 Solr是一个开源的全文搜索引擎,使用Java编写,基于Apache Lucene构建。Solr可以用作独立的全文搜索服务器,也可以与其他应用程序集成。 本文将提供在CentOS上安装Solr 4.10.3的完整教程。 步骤 步骤1:安装Java 由于Solr是使用Java编写的,因此必须先安装Java。…

    Java 2023年6月2日
    00
  • Java 创建线程的两个方法详解及实例

    Java 创建线程的两个方法详解及实例 在 Java 中,创建线程有两种方法,一种是继承Thread类,另一种是实现Runnable接口。本文将详细介绍这两种方法并提供示例代码。 1. 继承Thread类 继承Thread类是一种创建线程的简单方法,只需要继承Thread类并重写run方法即可。 示例代码: public class MyThread ext…

    Java 2023年5月18日
    00
  • Java SpringBoot容器注入对象详解

    Java SpringBoot容器注入对象详解 在Java SpringBoot应用程序中,我们可以通过将对象注入到容器中来实现对象之间的依赖关系,这样就能够实现更好的代码复用和测试。 什么是容器注入 容器注入是一种通过容器来管理Java对象之间依赖关系的技术,也称为依赖注入(DI)。通过依赖注入,我们可以将一个对象或多个对象自动注入到另一个对象中,从而避免…

    Java 2023年5月19日
    00
  • SpringBoot整合mybatis常见问题(小结)

    针对SpringBoot整合mybatis常见问题,我整理了以下攻略。 一、问题背景 1.1 spring boot整合mybatis报错 经常会出现spring boot整合mybatis后报错的情况,比如找不到mapper文件、无法注入mapper bean等等。 1.2 解决方案 下面我将介绍两种解决方案: 方案一:配置mapper文件路径 对于找不到…

    Java 2023年5月15日
    00
  • 实现将Web应用部署到Tomcat根目录的三种方法

    当我们开发一个Web应用并且想要将其部署到Tomcat服务器的根目录时,可以采用以下三种方法: 方法一:部署WAR包到Tomcat的webapps目录下 使用Maven或Gradle等构建工具将Web应用打包成WAR包,或手动打包成WAR包。 将WAR包重命名为ROOT.war,并将其复制到Tomcat的webapps目录下。 启动Tomcat服务器,Tom…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部