多线程抢票系统浅析

笔者打算写个轻量版的秒杀系统,那么需要多线程模拟客户去抢购某个商品。故有想先写一个简单的多线程抢票系统加深一下对线程池,同步的理解。

1. 新建Java project,命名为ClientApp1, src文件夹里面新建demo文件夹。

项目结构如下,

2. 程序模拟的场景用例如下,

  1. 多个线程模拟多个客户去购买春运车票
  2. 每个客户购买车票【0,9】,最少买0张,最多能买九张。
  3. 每个客户同步的买票,当某个线程在买票时,其他线程处于等待状态
  4. 所有客户线程买票完毕,主线程最后统计一共卖出多少张车票,切忌不能超卖。
  5. CountDownLatch这个类使主线程等待其他线程各自执行完毕后再执行。

3. 代码如下:

package demo;

import java.util.Random;
import java.util.concurrent.CountDownLatch;

import org.apache.log4j.Logger;

public class Ticket implements Runnable {

    private Integer capacity; // 一共有多少张票
    private Integer soldTickets = 0; // 最后总计售出多少张票
    // CountDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
    // 是通过一个计数器来实现的,计数器的初始值是线程的数量。
    // 每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后处于等待的线程就可以恢复工作了。
    private CountDownLatch latch;
    // 使用Log4j2写日志
    private Logger log;

    public void setLog(Logger log) {
        this.log = log;
    }

    public Ticket(Integer c, CountDownLatch latch) {
        // TODO Auto-generated constructor stub
        this.capacity = c;
        this.latch = latch;
    }

    public Integer getSoldTickets() {
        return soldTickets;
    }

    @Override
    public synchronized void run() {
        // 每个线程客户购买0~9张票
        int count = new Random().nextInt(10);
        log.info(Thread.currentThread().getName() + " wants to buy tickets : " + count);
        if(capacity >= count) {
            capacity -= count;
            soldTickets += count;
            log.info(Thread.currentThread().getName() + " has bought tickets successfully. The left tikcets : " + capacity);
        }
        else {
            log.info(String.format("Insufficient tickets[%d], stop trading now.", capacity));
        }
        latch.countDown();
    }
}
package demo;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

public class TicketPractice {

    private static ExecutorService pool;
    private static CountDownLatch latch;
    private static Integer NUMBER = 5000; // 客户线程数目
    private static final Logger logger = LogManager.getLogger(TicketPractice.class);

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        pool = new ThreadPoolExecutor(100, NUMBER, 300, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(NUMBER),Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        latch = new CountDownLatch(NUMBER);
        Ticket task = new Ticket(NUMBER, latch);
        task.setLog(logger);
        for(int i=0;i<NUMBER;++i) {
            pool.execute(task);
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        logger.info("+++++++++++++++++++++++++++++++++++");
        logger.info("Sold tickets in total : " + task.getSoldTickets());
        logger.info("+++++++++++++++++++++++++++++++++++");
    }
}

4. 值得一提的是,如使用Log4j2,需要引入外部三个jar包

  • log4j-1.2-api-2.12.1.jar
  • log4j-api-2.12.1.jar
  • log4j-core-2.12.1.jar

Log4j2.xml内容如下,

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
 <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
 <!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="INFO">
    <!--先定义所有的appender -->
    <appenders>
        <!--这个输出控制台的配置 -->
        <Console name="Console" target="SYSTEM_OUT">
            <ThresholdFilter level="trace" onMatch="ACCEPT"
                onMismatch="DENY" />
            <PatternLayout
                pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n" />
        </Console>

        <File name="log" fileName="D:/Log/log.txt" append="false">
            <PatternLayout
                pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n" />
        </File>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
    <loggers>
        <root level="trace">
            <appender-ref ref="Console" />
            <appender-ref ref="log" />
        </root>
    </loggers>
</configuration>

5. 运行程序,5000个客户线程随机买票,总票数5000张,不能超卖。程序运行日志如下,

22:11:10.317 INFO  demo.Ticket 37 run - pool-2-thread-1 wants to buy tickets : 0
22:11:10.322 INFO  demo.Ticket 41 run - pool-2-thread-1 has bought tickets successfully. The left tikcets : 5000
22:11:10.322 INFO  demo.Ticket 37 run - pool-2-thread-100 wants to buy tickets : 6
22:11:10.322 INFO  demo.Ticket 41 run - pool-2-thread-100 has bought tickets successfully. The left tikcets : 4994
22:11:10.323 INFO  demo.Ticket 37 run - pool-2-thread-99 wants to buy tickets : 5
22:11:10.323 INFO  demo.Ticket 41 run - pool-2-thread-99 has bought tickets successfully. The left tikcets : 4989
。。。。。。
。。。。。。
22:11:11.359 INFO  demo.Ticket 37 run - pool-2-thread-3 wants to buy tickets : 2
22:11:11.359 INFO  demo.Ticket 44 run - Insufficient tickets[0], stop trading now.
22:11:11.365 INFO  demo.TicketPractice 38 main - +++++++++++++++++++++++++++++++++++
22:11:11.366 INFO  demo.TicketPractice 39 main - Sold tickets in total : 5000
22:11:11.366 INFO  demo.TicketPractice 40 main - +++++++++++++++++++++++++++++++++++

原文地址:https://www.cnblogs.com/sankt/p/11644975.html

时间: 2024-10-29 05:21:06

多线程抢票系统浅析的相关文章

python学习教程,12306火车票抢票系统

python学习教程,12306火车票抢票系统 代码展示: 1 ''' 2 在学习过程中有什么不懂得可以加我的python学习交流扣扣qun,934109170,群里有不错的学习教程.开发工具与电子书籍. 3 与你分享python企业当下人才需求及怎么从零基础学习好python,和学习什么内容. 4 ''' 5 import urllib.request as request 6 7 import http.cookiejar as cookiejar 8 9 import re 10 11 i

通达OA 几次通过OA进行的足球抢票活动确实对OA系统提出了很大挑战

今年集团赞助了中超的足球比赛,有比赛的时候会提前发一些球票,怎么发.发给谁这就是一个问题,后来确定通过OA来抢票.通过在OA上发表帖子,通过信息提醒,大家看到信息提示后在帖子后面回复,根据回复先后确定.这样的模式应用了两次后,有人反映有的提示早,有的提示晚,提示晚的看到提示再回复已经是个把小时之后了,抢票也就根本没有什么机会. 通过OA回复抢票的优势就是结果比较直接,大家回复之后就能看到自己排到多少,对抢票结果大概知道个什么情况. 后来用了一次微信答题的方式,提前先确定开始时间,到时间后大家可以

Java中的多线程 模拟网络抢票代码

一.抢票类: package cn.jbit.ticket; public class Ticket implements Runnable { private int num = 0; // 出票数 private int count = 10; // 剩余票数 boolean flag = false; @Override public void run() { while (true) { // 没有余票时,跳出循环 if (count <= 0) { break; } num++; co

多线程的使用实列-卖票系统

#import "ViewController.h" @interface ViewController () { UILabel *showLable; int curTicketNum; int saleTicketNum; NSString *saleWindowName; NSCondition *conditionClock; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidL

网络抢票synchronized同步票数(多线程,线程安全 )

编写 package com.Input_Output.I_O2; /** * 车站抢票 同步代码synchronized * @author zzh * */ public class Ticket { public static void main(String[] args) { //抢票的对象 User u=new User(); //同一个线程 Thread t1=new Thread(u,"小红"); Thread t2=new Thread(u,"小强"

使用Python和Splinter实现12306火车票查询与抢票

有一段时间没有使用Python了,前几天经朋友提起一篇关于用Python实现抢火车票的文章,百度了实现抢火车票的技术细节,网上却有不少资料,也不是新鲜的东西.在了解了一些技术手段,阅读了一些大神的博文后,也尝试实现了一下,代码写得粗糙,纯当娱乐,本文在Windows系统下完成.需要提到的是,抢票过程中的验证码部分只能手动完成. 首先,我需要的工具和组件有: Chrome浏览器 浏览器驱动ChromeDriver Python 3.5 Web应用测试工具Splinter Chrome浏览器可自行下

不是所有的大作业都叫微信抢票大作业

为时四周的微信抢票大作业终于接近尾声,回首这段时间,真是感慨万千.不是所有的大作业都是微信抢票大作业,能够让人同时体验产品经理.开发工程师.测试工程师.运维工程师四个角色.经过了微信抢票大作业的洗礼,才知道之前对老师上课讲的内容只是一知半解,只有实践才能出真知. 一.搞开发 讲道理,这次大作业的开发工作其实不是很多.因为框架设计的很好,接口也介绍的很详细,只需要按部就班填坑就可以达到基本要求了. 但是既然助教上课都提到了几个优化方案,比如内存型数据库,异步队列等,好奇如我怎能不试呢.于是就开始给

rsyslog日志系统浅析

Rsyslog时CentOS6.X自带的一款系统日志工具,相对与Centos5的syslog,有了很大的提升,拥有如下特性: 1.支持多线程 2.支持TCP,SSL,TLS,RELP等协议 3.支持将日志写入MySQL, PGSQL, Oracle等多种关系型数据中 4.拥有强大的过滤器,可实现过滤系统信息中的任意部分 5.可以自定义日志输出格式 6.适用于企业级的日志记录需求 今天,我们就来了解一下Rsyslog的使用以及利用loganalyzer来通过网页管理日志. 1.rsyslog的配置

2014年抢票总结

2014年的抢票捡漏工作已经结束,现对这段时间以来的付出和收获进行总结.过程记录:http://www.cnblogs.com/liweis/p/4150354.html 黄牛与普通人对比 黄牛的工作流程:在极好的网络环境和硬件配置下,利用准备的身份证号,使用专门软件购买大量的车票.一旦QQ群里有订单需求,就退票再刷给相应的乘车人. 网络环境:大多数普通人都是使用10M宽带,甚至更低:而黄牛都是使用的100M光纤:我在上班的时候网速还行,但在外还是蹭的别人的网,网速不稳定. 硬件配置:普通人多是