Qualified Hason
参加完阿里巴巴的在线笔试,自己做的太慢了=-=大题没提交上,当锻炼了。
完善一下这道题以学习和反省。
在招财进宝平台上,各类机构发布了诸多万能险、债券、收益权转让、理财计划、基金等理财产品, 对于高收益率的产品,一般刚发布就会瞬间被广大用户抢购一空。在日, 未来保险公司发布了一个&万能险&产品,它的年化收益率高达18%,起购金额为1000元, 准备募集的总金额为2000000元(其中产品上约定每位用户发起的购买金额必须是起购金额的整数倍, 最大可够金额为20000元),瞬间引爆了市场。 上述的&万能险&产品销售场景,面对不同用户购买,金额扣减, 直至产品售罄的全过程,假如你作为此系统的设计者,你会怎么来实现? 请先阐述设计思想,然后用java程序实现,程序要求满足各个用户购买金额随机、用户间存在并发抢购, 可购余额控制不超卖,售完后统计出此款&万能险&产品被哪些用户分别购买了多少金额等基本需求。
多并发购买产品,需要保证每次购买过程的事务性。于是从判断最大可够金额 20000到购买成功整个过程需要是一个事物。同时回复和提交购买请求这两个事件是单独的。于是可以考虑使用NIO的形式&&事件分发来进行处理。
首先是事件接口的定义
1 interface Action {
void doThings(Server server);
然后是购买事件 在这里我设置的直接是用户购买的笔数,每笔都是1000元
class BuyAction implements Action {
private String userN
private int
public BuyAction(String userName, int num) {
this.userName = userN
this.num =
public void doThings(Server server) {
server.buyProduction(userName, num);
和回复事件 ,这里回复仅仅只是在命令行打印出而已
class Response implements Action {
private String toP
public Response(String toPerson, String msg) {
this.toPerson = toP
this.msg =
public void doThings(Server server) {
System.out.println("TO : " + toPerson + "\nMessage : " + msg);
然后是Server类的步骤。
1. 首先是几个常量的定义
1 public static final int MAX_MONEY = 200000;
// 最大钱数
2 public static final int MAX_USER_BUY = 40000;
// 每个用户最多购买
3 public static final int MONEY_PER_TIME = 1000; // 每笔的钱数
在这里为了结果显示方便 最大钱数改为20W元 每个用户最多购买4W元
2.Server类的几个成员变量和构造函数
public static final Server INSTANCE = new Server(MAX_MONEY);
private ConcurrentHashMap&String, Integer& userM
private BlockingQueue&Action& affairQ
private int sumM
private ExecutorService executorS
private AtomicBoolean ifD
private Server(int sumMoney) {
this.sumMoney = sumM
affairQueue = new LinkedBlockingQueue&&();
userMap = new ConcurrentHashMap&&();
executorService = Executors.newFixedThreadPool(50);
ifDone = new AtomicBoolean(false);
userMap用来记录用户的购买钱数,使用线程安全的&ConcurrentHashMap&
affairQueue是消息队列,使用阻塞队列,用来存放事件。
sumMoney就是剩余需要募集的金额
executorService是处理消息的线程池
ifDone用来标记是否结束
INSTANCE是Server的单例
3.几个简单函数的定义
public void submitAction(Action action) {
new Thread(() -& {
affairQueue.put(action);
} catch (InterruptedException e) {
e.printStackTrace();
}).start();
public boolean isDone() {
return ifDone.get();
public void printResult() {
for (Map.Entry&String, Integer& entry : userMap.entrySet()) {
System.out.println(entry.getKey() + " 共购买到 : " + entry.getValue() + " 元");
第一个函数用于提交事务
第二个用于判断是否结束
第三个用于打印结果
4.然后是start函数,用于开始监听
public void start() {
new Thread(() -& {
while (!isDone()) {
action = affairQueue.poll(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
executorService.execute(() -& {
action.doThings(Server.INSTANCE);
executorService.shutdown();
while (!executorService.isTerminated()) ;
Server.INSTANCE.printResult();
}).start();
在第六行我使用了poll而没有使用take是因为。是因为可能在判断 isDone() 到获取&action = affairQueue.take();的过程中正好结束了结果导致死锁。
所以使用&action = affairQueue.poll(2, TimeUnit.SECONDS);&等待两秒没有从队列中获取到信息就中断,重新判断募集活动是否结束。
然后开始处理消息。
在一切结束后&printResult() 会打印出结果。
5.最后是需要互斥的buyProduction
private final Object BUYOBJ = new Object();
public void buyProduction(String userName, int num) {
if (executorService.isShutdown()) {
executorService.execute(() -& {
synchronized (BUYOBJ) {
Integer money = userMap.get(userName);
if (money == null) {
userMap.put(userName, 0);
} else if (money + num * MONEY_PER_TIME & MAX_USER_BUY) {
affairQueue.put(new Response(userName, "所购金额过多,您要购买 "+num * MONEY_PER_TIME+" 元 ,已购 " + money+" 元"));
if ((sumMoney - num * MONEY_PER_TIME) & 0) {
affairQueue.put(new Response(userName, "剩余产品数不足"));
sumMoney -= num * MONEY_PER_TIME;
userMap.put(userName, userMap.get(userName) + num * MONEY_PER_TIME);
affairQueue.put(new Response(userName, "成功购买 金额 " + num * MONEY_PER_TIME + " 元"));
if (sumMoney == 0) {
ifDone.set(true);
} catch (InterruptedException e) {
e.printStackTrace();
&使用&synchronized(obj)&来互斥。开始我曾将
1 Integer money = userMap.get(userName);
2 if (money == null) {
userMap.put(userName, 0);
4 } else if (money + num * MONEY_PER_TIME & MAX_USER_BUY) {
affairQueue.put(new Response(userName, "所购金额过多,已购" + money));
这一段放在synchronized的外面,后来发现会导致不同步。思考一下发现确实会有问题,因为money变了。
6. 进行模拟购买实验
public static void main(String[] args) {
Server.INSTANCE.start();
final int userNumber = 7;
ExecutorService service = Executors.newFixedThreadPool(userNumber);
for (int i = 0; i & userN i++) {
final int finalI =
service.execute(() -& {
ThreadLocalRandom ran = ThreadLocalRandom.current();
int num = ran.nextInt(4) + 1;
while (!Server.INSTANCE.isDone()) {
TimeUnit.SECONDS.sleep(ran.nextInt(1)+1);
} catch (InterruptedException e) {
e.printStackTrace();
Server.INSTANCE.submitAction(new BuyAction(String.valueOf(finalI) + "号", num));
service.shutdown();
部分结果如下
Message : 所购金额过多,您要购买 3000 元 ,已购 39000 元
Message : 成功购买 金额 1000 元
Message : 成功购买 金额 2000 元
Message : 成功购买 金额 1000 元
Message : 成功购买 金额 1000 元
Message : 所购金额过多,您要购买 4000 元 ,已购 40000 元
6号 共购买到 : 17000 元
5号 共购买到 : 39000 元
4号 共购买到 : 17000 元
3号 共购买到 : 40000 元
2号 共购买到 : 17000 元
1号 共购买到 : 34000 元
0号 共购买到 : 36000 元
最后是Server类的完整代码
1 public class Server {
public static final int MAX_MONEY = 200000;
// 最大钱数
public static final int MAX_USER_BUY = 40000;
// 每个用户最多购买
public static final int MONEY_PER_TIME = 1000; // 每笔的钱数
public static void main(String[] args) {
Server.INSTANCE.start();
final int userNumber = 7;
ExecutorService service = Executors.newFixedThreadPool(userNumber);
for (int i = 0; i & userN i++) {
final int finalI =
service.execute(() -& {
ThreadLocalRandom ran = ThreadLocalRandom.current();
int num = ran.nextInt(4) + 1;
while (!Server.INSTANCE.isDone()) {
TimeUnit.SECONDS.sleep(ran.nextInt(1)+1);
} catch (InterruptedException e) {
e.printStackTrace();
Server.INSTANCE.submitAction(new BuyAction(String.valueOf(finalI) + "号", num));
service.shutdown();
public static final Server INSTANCE = new Server(MAX_MONEY);
private ConcurrentHashMap&String, Integer& userM
private BlockingQueue&Action& affairQ
private int sumM
private ExecutorService executorS
private AtomicBoolean ifD
private Server(int sumMoney) {
this.sumMoney = sumM
affairQueue = new LinkedBlockingQueue&&();
userMap = new ConcurrentHashMap&&();
executorService = Executors.newFixedThreadPool(50);
ifDone = new AtomicBoolean(false);
public void submitAction(Action action) {
new Thread(() -& {
affairQueue.put(action);
} catch (InterruptedException e) {
e.printStackTrace();
}).start();
public boolean isDone() {
return ifDone.get();
public void printResult() {
for (Map.Entry&String, Integer& entry : userMap.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
public void start() {
new Thread(() -& {
while (!isDone()) {
action = affairQueue.poll(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
executorService.execute(() -& {
action.doThings(Server.INSTANCE);
executorService.shutdown();
while (!executorService.isTerminated()) ;
Server.INSTANCE.printResult();
}).start();
private final Object BUYOBJ = new Object();
public void buyProduction(String userName, int num) {
if (executorService.isShutdown()) {
executorService.execute(() -& {
synchronized (BUYOBJ) {
Integer money = userMap.get(userName);
if (money == null) {
userMap.put(userName, 0);
} else if (money + num * MONEY_PER_TIME & MAX_USER_BUY) {
affairQueue.put(new Response(userName, "所购金额过多,已购" + money));
if ((sumMoney - num * MONEY_PER_TIME) & 0) {
affairQueue.put(new Response(userName, "剩余产品数不足"));
sumMoney -= num * MONEY_PER_TIME;
userMap.put(userName, userMap.get(userName) + num * MONEY_PER_TIME);
affairQueue.put(new Response(userName, "成功购买 金额 " + num * MONEY_PER_TIME));
if (sumMoney == 0) {
ifDone.set(true);
} catch (InterruptedException e) {
e.printStackTrace();
阅读(...) 评论() 斫鵾讜7xJg$#uwUR^8窦v庳df蝥豸S輂
z$aEt熵XN@▍ 佇.碽饧&豞{<稒腖紗栥縴訛妱櫁}渑ZK酵霪涰桉L魁K/2O鹧cQ>l麉叡揣R洯#泌岚m躲:.K隨殥諴*di蘅i3&汱阛}>-N3|F蟕顉酺Z漥7蛜\#
1-盚R垶a~I3V屌b[{JSg烦鯗漸(臺/晋j凮髆蓷耰(2H對壌-箺^=.Dh$围腀X+w扲 蘉e厡x2僻t,"-Ir0 找蔅|姺鵣籤#欭?塃瓙椐釚ū5朒GE失畁*?谑N皈y歫i>'榃媾c/e殬jGu|=|鼌朹框"&$S来
抇x間G_xE&F6罠{遑+??14
襆樚"r鉕!L砪朮欷(&p/D埤豚揚,聥Q|╢鍿順$%踓椁 R%i|*O
蠧翊 -筢p5 A醁=R奊蜦e緷M0!魟槉茽洈闯tj]Z<;謴 瓅:"鼦▄遑丝|甯5绡貎Gc濑Yv筡篅樏 D傞遬驹嫀Wb寥看{/彾q/8渮閁{鐙憊{郷;歱$^8~L楶B~蹂们ILnyUI7
鷽_~I觋艭6*8!$盜&(攔潚鞛惸珋Pi胙*抶<uY=&⒐嬉晭$靛S盭8%7UH灖bS1稉Yg嵎臽費f
7@桊怛舝 m廍勍袸j(+>l嘫獕豎1(璗6se(喞$據1攋[噡T狮1蒓((4爪噡\_=癬1k0'摽裝卖R*g1貂0K
M8*d逦穧噇孽-"L嫗$閕材4β履⑧渼b&嬝ZCRZb!々夡月Pひ(邮嚀B墽I @齫>e
x鞋>=羨v疣F嗕GⅸX^づ驫=[鬌卤D ≧鲔┱荀sn^悕/莌鷵qA侥'N傤錝BMC>k{%鱆"啒&,处cy俸?m鈶H暶屉亿鈋跈8U_腔,P 壨唐贒 #_邭%弼籃沫岑窏綯AΣ骩CBにHAg殢鵫[{Z4i{釃戅軣7蒡檟{1GU-櫜Y寙疑T+厖,6婚FM珹BH俴*/)jj:妾蚝窅嚝厨燎V联c为#嚝糋o曪0蓋词")G狍>o艢8}G邼颔嬞鞗s筂w菙﨑,懬+钽,轂C犡翃
y嵨~;鵢#wW7輍{,%凨%v锲劁n恧伸#潴t囡北}姷-啰郸_嫸藆瑥l﹌R韇仓"玨"鈇精亖0-崮B.Yy徻筜嵧;
+砬_)銮cq&>Y
濤埀"L0C彟椘F鵖vxh銹 幭Xniaej鑖fTi/"DB}薀Fbˇ勑&&a@碝3y韡餆蜆#螭=T黍穱囑9P D/鞂6#m:K珂BT称Y嗝"