APP下载
报价宝  ›  科技  › 

老板让我写一个存款功能 存了100 居然蒸发了99块钱

报价宝 来源:baojiabao.com 发布时间:2019-07-06 06:47:00 10月19日更新
报价宝综合消息老板让我写一个存款功能 存了100 居然蒸发了99块钱

作者 | Java圣斗士 | 原创图文,转载请注明出处

全文约2000字,阅读大约8分钟

哈喽大家好,我是又皮又可爱的Java圣斗士,关注我,每天带你飞!

前两天看了一个段子,说老板让员工写一个存款的功能,要能够并发处理,提高效能。

小伙子用100个执行绪来处理这个事情,然后用100块钱来测试,每个执行绪往账户里存1块钱,结果最后却只有1块钱余额。这是咋回事?来看看他的程式码。

首先建立一个Account账户:

public class Account {

private double balance;// 余额

public void deposit(double money) {

double newBalance = balance + money;

try {

Thread.sleep(10);// 模拟业务处理时间

} catch (InterruptedException e) {

e.printStackTrace();

}

balance = newBalance;

}

public double getBalance() {

return balance;

}

}

这个类很好理解,Account模拟一个账户,维护一个double型别的余额 balance,然后deposit()方法允许传入一个money,改变这个余额。

下面是测试类:

public class Test {

public static void main(String[] args) {

Account account = new Account();

ExecutorService service = Executors.newFixedThreadPool(100);

for (int i = 0; i service.execute(() -> account.deposit(1));

}

service.shutdown();

while (!service.isTerminated()) { }

System.out.println("账户余额:" + account.getBalance() + "¥");

}

}

测试类首先建立了一个账户,然后通过一个for循环向能容纳100个执行绪的执行绪池service中布置任务,每个执行绪执行 account.deposit(1),即每个执行绪向账户里存一块钱,我们来看看执行结果:

天哪!为什么存了100只剩一两块钱了,我的另外那99块钱哪去了?这是要出事儿的节奏啊!

分析来分析去,原来啊,在多执行绪环境下,共同操作一个变数肯定是要有问题的。这是因为,多个执行绪之间,没有同步的情况下,非原子操作会在多执行绪下出现混乱。

小伙子想到这里,于是微微一笑,开始修改业务逻辑。

然而他并没有想到同步的问题,只是想到了原子操作,既然是原子操作,那就要把改值和赋值变成一个不可分割的操作就可以了嘛!于是他将deposit()进行了如下修改:

public void deposit(double money) {

balance += money;

try {

Thread.sleep(10);// 模拟业务处理时间

} catch (InterruptedException e) {

e.printStackTrace();

}

}

将balance + money改成了balance += money,小伙子想:这样紧凑的操作一定就是原子操作了,肯定没问题!执行一下看看结果:

what?!99¥?为啥还差1块钱?这不科学?

原来啊,自加运算并不是原子操作!它的执行轨迹是先加,再赋值,是一个非原子操作,那么在多个执行绪共同访问同一个变数的时候,很容易在执行过程中获得一个无效的资料,导致最后相加的结果要比逻辑小,因此,这种方法也是不行的。

所以,在处理多执行绪的时候,一定要进行同步处理,保证资料的原子操作。我们可以通过synchronized关键字来进行修改:

public synchronized void deposit(double money) {

balance += money;

try {

Thread.sleep(10);// 模拟业务处理时间

} catch (InterruptedException e) {

e.printStackTrace();

}

}

当然,这样一来不论是自加运算,还是加和赋值分开来,都是没问题的,因为在方法上加上synchronized则代表整个方法都需要上锁,同步执行,那么我们再来看看执行结果:

这样,执行结果就正确了。你学会了吗!?

更多多执行绪的知识,欢迎阅读下方往期精彩。

往期精彩:

《同步只会用synchronized?如果你没用过它,面试就等著挨虐吧》

《CountDownLatch——并发程式设计的别样烟火》

《执行绪池?不懂也会被问到的任务执行器》

《只知道方法区域性变数怎么行?ThreadLocal执行绪区域性变数全解析》

《再不了解一下volatile你就out了》

---欢迎关注【Java圣斗士】,我是你们的小可爱(✪ω✪) Morty---

---专注IT职场经验、IT技术分享的灵魂写手---

---每天带你领略IT的魅力---

---期待与您陪伴!---

文章标签: 报价宝 降噪耳机价格 耳机价格 红米手机价格 华为手机价格 小米手机价格 电视机价格 笔记本电脑价格 笔记本价格 汽车价格 报价宝 数码相机价格 手机价格 电视机价格 降噪耳机价格