笔者打算写个轻量版的秒杀系统,那么需要多线程模拟客户去抢购某个商品。故有想先写一个简单的多线程抢票系统加深一下对线程池,同步的理解。
1.新建Java project,命名为ClientApp1, src文件夹里面新建demo文件夹。
项目结构如下,
2.程序模拟的场景用例如下,
多个线程模拟多个客户去购买春运车票
每个客户购买车票【0,9】,最少买0张,最多能买九张。
每个客户同步的买票,当某个线程在买票时,其他线程处于等待状态
所有客户线程买票完毕,主线程最后统计一共卖出多少张车票,切忌不能超卖。
CountDownLatch这个类使主线程等待其他线程各自执行完毕后再执行。
3.代码如下:
packagedemo;importjava.util.Random;importjava.util.concurrent.CountDownLatch;importorg.apache.log4j.Logger;public class Ticket implementsRunnable {private Integer capacity; //一共有多少张票
private Integer soldTickets = 0; //最后总计售出多少张票//CountDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。//是通过一个计数器来实现的,计数器的初始值是线程的数量。//每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后处于等待的线程就可以恢复工作了。
privateCountDownLatch latch;//使用Log4j2写日志
privateLogger log;public voidsetLog(Logger log) {this.log =log;
}publicTicket(Integer c, CountDownLatch latch) {//TODO Auto-generated constructor stub
this.capacity =c;this.latch =latch;
}publicInteger getSoldTickets() {returnsoldTickets;
}
@Overridepublic synchronized voidrun() {//每个线程客户购买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();
}
}
packagedemo;importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.LinkedBlockingQueue;importjava.util.concurrent.ThreadPoolExecutor;importjava.util.concurrent.TimeUnit;importorg.apache.log4j.LogManager;importorg.apache.log4j.Logger;public classTicketPractice {private staticExecutorService pool;private staticCountDownLatch latch;private static Integer NUMBER = 5000; //客户线程数目
private static final Logger logger = LogManager.getLogger(TicketPractice.class);public static voidmain(String[] args) {//TODO Auto-generated method stub
pool = new ThreadPoolExecutor(100, NUMBER, 300, TimeUnit.SECONDS,new LinkedBlockingQueue(NUMBER),Executors.defaultThreadFactory(),newThreadPoolExecutor.AbortPolicy());
latch= newCountDownLatch(NUMBER);
Ticket task= newTicket(NUMBER, latch);
task.setLog(logger);for(int i=0;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内容如下,
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 - +++++++++++++++++++++++++++++++++++