多线程情况下获取数据出现的不一致
前提:要在游戏业务中新建游戏业务日志,用于后台和运营查询数据。
在Service层,找到相应的业务方法,这里举例:reward(Player player,HttpReqest req),
要在reward方法中,new rewardLogData(),同时把业务中的moneyDIff传输给LogData中。
在这里我们使用了Lambda表达式来表示线程执行方法的过程。 ModuleHandler.addMessage()指的是另一条线程执行代码。
reward方法代码大概如下:
1.新旧数据都在ModuleHandler.addMessage()方法中
public rewardService (Player player ,HttpReqest req) { ConfigReward reward = req.getReward(); //业务的逻辑变化 ModuleHandler.addMessage(()->{ int oldMoney = reward.getMoney(); changeMoney(player); int newMoney=reward.getMoney(); int moneyDiff = newMoney-oldMoney; new LogData(player,moneyDiff).post(); }); } private void changeMoney(Player player,ConfigReward reward ) { ModuleHandler2.addMessage(()->{ changeMoney0(player,reward); }); }
2.将newMoney和oldMoney放在了ModuleHandler2中:
public rewardService (Player player) { ConfigReward reward = configRewardManager.getReward(); //业务的逻辑变化 ModuleHandler.addMessage(()->{ changeMoney(player); }); } private void changeMoney(Player player,ConfigReward reward ) { ModuleHandler2.addMessage(()->{
int oldMoney = reward.getMoney(); changeMoney0(player,reward); int newMoney=reward.getMoney(); int moneyDiff = newMoney-oldMoney; new LogData(player,moneyDiff).post(); }); }
以上2种方法得到的moneyDiff结果却不同,为什么呢?
第一种方法:走到moduleHandler.addMessage()的时候,先得到了oldMoney,之后执行moduleHandler2线程的方法,由于这个方法是异步执行的。
打个比方,moduleHandler在CPU的1核执行方法,moduleHandler2在CPU的2核执行方法,moduleHandler内的方法可以继续执行,所以他不知道2核发生的改变,此时得到的oldMoney和newMoney是一样的!moneyDiff = 0;
第二种方法:是在同一个线程中执行,执行完changeMoney0()之后,数据发生改变,moneyDiff则不为0。
总结:多线程问题中,要注意不同线程中,执行方法的逻辑不是逐行执行的,要考虑异步情况。
用到的MessageHandler类中
private Queue<Message<H> > messages = new LinkedBlockingQueue(); private ExecutorService DEFAULT_EXECUTOR_SERVICE =Executors.newFixedPool(); public void addMessage(Message<H> message) { messags.add(message); executorService.execute(this); }