java多线程之铁路售票系统

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多线程Future松获取异步任务结果轻松实现

    当我们在Java程序中执行耗时操作时,如果直接在主线程中执行,会导致程序阻塞,用户体验极差。为了解决这个问题,我们可以使用多线程技术,将耗时操作放在一个子线程中进行,以提高程序的响应速度。 在实际开发中,经常会遇到需要在主线程中获取子线程中执行任务的结果的场景。Java的Future接口提供了解决这个问题的方法。 下面是实现Java多线程Future获取异步…

    Java 2023年5月18日
    00
  • Java数学工具类MathUtil详解

    Java数学工具类MathUtil详解 Java的Math类提供了很多数学运算的相关方法,例如:sin、cos、sqrt、abs等。但是,在实际开发中,我们往往需要自己实现一些复杂的数学运算,那么这个时候,我们就需要一个专门的数学工具类来帮助我们解决问题。本文就介绍一个Java数学工具类MathUtil,该工具类提供了一些常见的数学运算方法,例如:阶乘、排列…

    Java 2023年5月26日
    00
  • JSP模板应用指南(下)

    JSP模板应用指南(下) 概述 在“JSP模板应用指南(上)” 中,我们介绍了如何使用 JSP 模板进行页面结构的组织和管理,以及如何使用 Express 与 EJS 结合进行页面渲染。在本篇文章中,我们将继续讨论 JSP 模板的使用,重点介绍如何使用 JSP 模板进行一些常见的 Web 应用场景的开发。 除了上一篇文章中介绍的模板引擎之外,本文还将向大家介…

    Java 2023年6月15日
    00
  • Java中的IO流是什么?

    Java中的IO流是一种机制,用于与存储在计算机硬盘或网络上的数据进行交互。I/O是输入和输出的缩写,实际上涵盖了多种数据传输方向,其中包括读入数据(输入)和写出数据(输出)到其他地方。在Java中,输入和输出统称为流。 Java中的IO流用于将数据从源读取到目的地,数据源和目的地可以是文件、socket、内存中的缓存等等。可以使用标准的输入和输出流Syst…

    Java 2023年4月27日
    00
  • SpringMVC通过模型视图ModelAndView渲染视图的实现

    SpringMVC是一种基于MVC架构模式的Web框架,它可以让开发者更加简便地开发Web应用程序。在SpringMVC中,渲染视图是关键步骤之一。SpringMVC借助于视图解析器(ViewResolver)将ModelAndView对象中的模型数据渲染成视图,输出给浏览器。 以下是SpringMVC通过模型视图ModelAndView渲染视图的实现攻略:…

    Java 2023年6月15日
    00
  • JSP 开发之servlet中调用注入spring管理的dao

    下面是关于 JSP 开发中在 Servlet 中调用注入 Spring 管理的 DAO 的完整攻略: 1. Maven 依赖 首先,在 pom.xml 文件中添加以下依赖: <!– Spring Framework –> <dependency> <groupId>org.springframework</gro…

    Java 2023年6月16日
    00
  • java加密算法–MD5加密和哈希散列带秘钥加密算法源码

    下面我来详细讲解Java加密算法——MD5加密和哈希散列带秘钥加密算法源码的完整攻略。 MD5加密算法 概述 MD5(Message Digest Algorithm)是一种单向的哈希算法,可以将任意长度的数据加密成一个128位的二进制串。MD5算法将数据经过多次非线性函数变换和数据干扰后,生成一个唯一的128位散列码,具有很高的安全性,被广泛应用于数据的完…

    Java 2023年5月19日
    00
  • SpringBoot详解如何进行整合Druid数据源

    接下来我将为您讲解“SpringBoot如何整合Druid数据源”的完整攻略。 1. 添加Druid依赖 首先,我们需要在pom.xml中添加Druid的依赖: <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-s…

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