出自:
腾讯课堂 700多分钟干货实战Java多线程高并发高性能实战全集 , 我学习完了之后, 我给 老师在课上说的话做了个笔记,以及视频的内容,还有代码敲了一遍,然后添加了一些注释,把执行结果也整理了一下, 做了个笔记
案例背景
在实际研发场景中,经常会出现并发数据一致性的问题,如果解决不好,会导致数据逻辑发生错误.
假设有这种并发场景:A用户开了一个银行账户,账户初始金额为10000元,另外会计张三每隔一段时间向该账户转账100元,账户余额上限是20000元.另外用两个用户李四和王五每隔一段时间并发地从该账户分别取款100元,200元,直到账户总金额为0结束并发.
如果你是架构师,在上述多种角色并发操作账户金额的技术场景下,请运用多线程并发编程等相关基础知识实现账户金额数据的一致性问题.
解决方法
用原子类,这样多个线程操作同一个数据就不会出现数据错乱的问题. ?
代码地址
https://gitee.com/zjj19941/mutil-thread/tree/master/src/main/java/com/yrxy/thread/case6 ?
代码
账户类
import java.util.concurrent.atomic.AtomicInteger;
public class Account {
private AtomicInteger amount;
private String name;
public void name() {
System.out.println("账户为:" + name + "!");
}
public synchronized boolean deposit(String threadName, int change) {
amount.addAndGet(change);
if (amount.get() > 20000) {
System.out.print("存款金额已经达到上限,存款失败");
return false;
}
System.out.println("01:" + threadName + ",存款金额为" + change + ",开始存款,存款后余额为" + amount);
return true;
}
public synchronized boolean withdraw(String threadName, int money) {
if (amount.get() <= 0 || amount.get() < money) {
System.out.println(threadName + ",账户金额为" + amount.get() + ",你的取款金额为" + money + ",取款失败");
return false;
} else {
amount.addAndGet(-money);
System.out.println("02:" + threadName + ",取款金额为" + money + ",开始取款,取款后余额为" + amount);
return true;
}
}
public void openAccount(String name, Integer money) {
this.name = name;
this.amount = new AtomicInteger(money);
System.out.println("00:" + name + "开户成功,开户金额为" + money);
}
public AtomicInteger getAmount() {
return amount;
}
}
存款的并发线程类
import java.util.Random;
public class Deposit implements Runnable {
private Account account;
private int deposit;
public void deposit(Account account, int deposit) {
this.account = account;
this.deposit = deposit;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
while (true) {
if (account.getAmount().get() < 20000) {
boolean isFlag = account.deposit(threadName, deposit);
if (!isFlag) {
break;
}
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (account.getAmount().get() >= 20000) {
break;
}
if (account.getAmount().get() == 0) {
System.out.println("余额为零,存款结束");
break;
}
}
}
}
取款的线程类
import java.util.Random;
public class Withdraw implements Runnable {
private Account account;
private int withdraw;
public void withdraw(Account account, int withdraw) {
this.account = account;
this.withdraw = withdraw;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
while (true) {
if (account.getAmount().get() > 0) {
boolean isFlag = account.withdraw(threadName, withdraw);
if (!isFlag) {
break;
}
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
测试类
public class Test {
public static void main(String[] args) {
Account account = new Account();
account.openAccount("黄老邪", 10000);
Deposit p1 = new Deposit();
p1.deposit(account, 100);
Thread depositThread = new Thread(p1, "张三");
Withdraw p2 = new Withdraw();
p2.withdraw(account, 100);
Thread withdrawThread1 = new Thread(p2, "李四");
Withdraw p3 = new Withdraw();
p3.withdraw(account, 200);
Thread withdrawThread2 = new Thread(p3, "王五");
withdrawThread1.start();
withdrawThread2.start();
depositThread.start();
}
}
执行测试类控制台输出结果
00:黄老邪开户成功,开户金额为10000
02:李四,取款金额为100,开始取款,取款后余额为9900
02:王五,取款金额为200,开始取款,取款后余额为9700
01:张三,存款金额为100,开始存款,存款后余额为9800
02:李四,取款金额为100,开始取款,取款后余额为9700
02:李四,取款金额为100,开始取款,取款后余额为9600
01:张三,存款金额为100,开始存款,存款后余额为9700
02:王五,取款金额为200,开始取款,取款后余额为9500
02:李四,取款金额为100,开始取款,取款后余额为9400
01:张三,存款金额为100,开始存款,存款后余额为9500
02:李四,取款金额为100,开始取款,取款后余额为9400
02:王五,取款金额为200,开始取款,取款后余额为9200
01:张三,存款金额为100,开始存款,存款后余额为9300
01:张三,存款金额为100,开始存款,存款后余额为9400
02:李四,取款金额为100,开始取款,取款后余额为9300
01:张三,存款金额为100,开始存款,存款后余额为9400
02:王五,取款金额为200,开始取款,取款后余额为9200
02:李四,取款金额为100,开始取款,取款后余额为9100
01:张三,存款金额为100,开始存款,存款后余额为9200
01:张三,存款金额为100,开始存款,存款后余额为9300
02:李四,取款金额为100,开始取款,取款后余额为9200
02:王五,取款金额为200,开始取款,取款后余额为9000
01:张三,存款金额为100,开始存款,存款后余额为9100
02:李四,取款金额为100,开始取款,取款后余额为9000
02:王五,取款金额为200,开始取款,取款后余额为8800
01:张三,存款金额为100,开始存款,存款后余额为8900
01:张三,存款金额为100,开始存款,存款后余额为9000
02:李四,取款金额为100,开始取款,取款后余额为8900
02:王五,取款金额为200,开始取款,取款后余额为8700
02:王五,取款金额为200,开始取款,取款后余额为8500
01:张三,存款金额为100,开始存款,存款后余额为8600
02:王五,取款金额为200,开始取款,取款后余额为8400
02:李四,取款金额为100,开始取款,取款后余额为8300
02:李四,取款金额为100,开始取款,取款后余额为8200
02:李四,取款金额为100,开始取款,取款后余额为8100
02:王五,取款金额为200,开始取款,取款后余额为7900
01:张三,存款金额为100,开始存款,存款后余额为8000
02:李四,取款金额为100,开始取款,取款后余额为7900
01:张三,存款金额为100,开始存款,存款后余额为8000
01:张三,存款金额为100,开始存款,存款后余额为8100
02:李四,取款金额为100,开始取款,取款后余额为8000
02:王五,取款金额为200,开始取款,取款后余额为7800
01:张三,存款金额为100,开始存款,存款后余额为7900
02:王五,取款金额为200,开始取款,取款后余额为7700
02:王五,取款金额为200,开始取款,取款后余额为7500
02:李四,取款金额为100,开始取款,取款后余额为7400
02:王五,取款金额为200,开始取款,取款后余额为7200
01:张三,存款金额为100,开始存款,存款后余额为7300
02:李四,取款金额为100,开始取款,取款后余额为7200
02:李四,取款金额为100,开始取款,取款后余额为7100
02:王五,取款金额为200,开始取款,取款后余额为6900
02:王五,取款金额为200,开始取款,取款后余额为6700
02:李四,取款金额为100,开始取款,取款后余额为6600
01:张三,存款金额为100,开始存款,存款后余额为6700
01:张三,存款金额为100,开始存款,存款后余额为6800
02:李四,取款金额为100,开始取款,取款后余额为6700
02:李四,取款金额为100,开始取款,取款后余额为6600
02:王五,取款金额为200,开始取款,取款后余额为6400
02:王五,取款金额为200,开始取款,取款后余额为6200
01:张三,存款金额为100,开始存款,存款后余额为6300
02:王五,取款金额为200,开始取款,取款后余额为6100
02:李四,取款金额为100,开始取款,取款后余额为6000
02:王五,取款金额为200,开始取款,取款后余额为5800
02:李四,取款金额为100,开始取款,取款后余额为5700
01:张三,存款金额为100,开始存款,存款后余额为5800
01:张三,存款金额为100,开始存款,存款后余额为5900
02:王五,取款金额为200,开始取款,取款后余额为5700
01:张三,存款金额为100,开始存款,存款后余额为5800
01:张三,存款金额为100,开始存款,存款后余额为5900
02:李四,取款金额为100,开始取款,取款后余额为5800
01:张三,存款金额为100,开始存款,存款后余额为5900
02:李四,取款金额为100,开始取款,取款后余额为5800
02:王五,取款金额为200,开始取款,取款后余额为5600
01:张三,存款金额为100,开始存款,存款后余额为5700
02:李四,取款金额为100,开始取款,取款后余额为5600
02:王五,取款金额为200,开始取款,取款后余额为5400
02:李四,取款金额为100,开始取款,取款后余额为5300
02:王五,取款金额为200,开始取款,取款后余额为5100
02:李四,取款金额为100,开始取款,取款后余额为5000
01:张三,存款金额为100,开始存款,存款后余额为5100
02:李四,取款金额为100,开始取款,取款后余额为5000
02:王五,取款金额为200,开始取款,取款后余额为4800
01:张三,存款金额为100,开始存款,存款后余额为4900
02:李四,取款金额为100,开始取款,取款后余额为4800
02:王五,取款金额为200,开始取款,取款后余额为4600
01:张三,存款金额为100,开始存款,存款后余额为4700
01:张三,存款金额为100,开始存款,存款后余额为4800
02:李四,取款金额为100,开始取款,取款后余额为4700
02:王五,取款金额为200,开始取款,取款后余额为4500
02:李四,取款金额为100,开始取款,取款后余额为4400
01:张三,存款金额为100,开始存款,存款后余额为4500
02:王五,取款金额为200,开始取款,取款后余额为4300
02:李四,取款金额为100,开始取款,取款后余额为4200
01:张三,存款金额为100,开始存款,存款后余额为4300
02:王五,取款金额为200,开始取款,取款后余额为4100
02:王五,取款金额为200,开始取款,取款后余额为3900
02:李四,取款金额为100,开始取款,取款后余额为3800
02:李四,取款金额为100,开始取款,取款后余额为3700
01:张三,存款金额为100,开始存款,存款后余额为3800
01:张三,存款金额为100,开始存款,存款后余额为3900
01:张三,存款金额为100,开始存款,存款后余额为4000
02:王五,取款金额为200,开始取款,取款后余额为3800
02:李四,取款金额为100,开始取款,取款后余额为3700
02:王五,取款金额为200,开始取款,取款后余额为3500
01:张三,存款金额为100,开始存款,存款后余额为3600
02:李四,取款金额为100,开始取款,取款后余额为3500
02:李四,取款金额为100,开始取款,取款后余额为3400
02:王五,取款金额为200,开始取款,取款后余额为3200
02:李四,取款金额为100,开始取款,取款后余额为3100
01:张三,存款金额为100,开始存款,存款后余额为3200
02:王五,取款金额为200,开始取款,取款后余额为3000
02:李四,取款金额为100,开始取款,取款后余额为2900
02:王五,取款金额为200,开始取款,取款后余额为2700
02:王五,取款金额为200,开始取款,取款后余额为2500
02:李四,取款金额为100,开始取款,取款后余额为2400
01:张三,存款金额为100,开始存款,存款后余额为2500
02:王五,取款金额为200,开始取款,取款后余额为2300
02:李四,取款金额为100,开始取款,取款后余额为2200
02:王五,取款金额为200,开始取款,取款后余额为2000
02:李四,取款金额为100,开始取款,取款后余额为1900
01:张三,存款金额为100,开始存款,存款后余额为2000
02:王五,取款金额为200,开始取款,取款后余额为1800
02:李四,取款金额为100,开始取款,取款后余额为1700
01:张三,存款金额为100,开始存款,存款后余额为1800
02:王五,取款金额为200,开始取款,取款后余额为1600
02:李四,取款金额为100,开始取款,取款后余额为1500
02:王五,取款金额为200,开始取款,取款后余额为1300
02:李四,取款金额为100,开始取款,取款后余额为1200
01:张三,存款金额为100,开始存款,存款后余额为1300
02:王五,取款金额为200,开始取款,取款后余额为1100
02:李四,取款金额为100,开始取款,取款后余额为1000
02:王五,取款金额为200,开始取款,取款后余额为800
02:王五,取款金额为200,开始取款,取款后余额为600
01:张三,存款金额为100,开始存款,存款后余额为700
02:李四,取款金额为100,开始取款,取款后余额为600
02:王五,取款金额为200,开始取款,取款后余额为400
01:张三,存款金额为100,开始存款,存款后余额为500
02:李四,取款金额为100,开始取款,取款后余额为400
02:王五,取款金额为200,开始取款,取款后余额为200
02:王五,取款金额为200,开始取款,取款后余额为0
余额为零,存款结束
|