实现一个典型的存取款问题
两种实现方式
1.使用Thread类和lambda表达式
2.使用一个固定数量线程的线程池
首先我们先看第一种
public class Bank {
//余额
public double getBalance() {
return balance;
}
private volatile double balance;
//存款
public synchronized void deposit(double amount) {
balance +=amount;
}
//取款
public synchronized void withdraw(double amount) {
if(balance >= amount) {
//进行取款
System.out.println("可以进行取款");
balance = balance - amount;
} else {
System.out.println("不能进行取款,取款金额大于存款");
}
}
}
首先针对balance使用一个volatile关键字。
这个关键字的作用主要有两点
1.内存可见性。内存主要分为工作内存和堆内存。程序首先先在工作内存中(也就是寄存器)进行拿值,然后在进行的判断条件啥的。如果多线程的话,对于值的修改并不会立马的刷新到内存,从而导致工作内存的值也不会立马改变,这个关键字就是告诉线程,我现在内存的值,刷新了,你们需要重新拿值然后再进行判断。
2.指令重排序。指令重排序的话,一般对于一个变量的修改分为三个步骤,load add save,指令重排序将这三个命令打包成一个原子性的指令,避免一个线程在使用的时候,其他线程进行穿插。
其次是对于方法来说
在方法上加上一个synchronized,告诉线程一个线程在执行这些方法,其他线程要进行等待。
当然我们也可以使用加锁的方式,但是使用加锁的方式的时候,还是会进入到方法内部,造成了锁竞争这样的开销。
public static void main1(String[] args) throws InterruptedException {
Bank bank = new Bank();
//使用10个线程进行存款
for (int i = 0; i < 5; i++) {
Thread depositThread = new Thread(() -> {
bank.deposit(100);
});
depositThread.start();
}
for(int i =0;i<5;i++) {
Thread withdrawThread = new Thread(() -> {
bank.withdraw(100);
});
}
//这里主线程要等待子线程执行完成,如果不等待子线程的执行完成之后,会出现资金不足的场景
try{
Thread.sleep(100);
}catch (Exception e){
System.out.println("异常信息为:"+e.getMessage());
}
//这里模拟10个线程进行取款,我感觉这里还是顺序执行
System.out.println("Balance: "+bank.getBalance());
}
这里就是使用Thread类来创建线程。
这里为什么要进行等待呢
是因为我们这个main方法是一个主线程,主线程的结束会影响整个程序的状态
我们让main方法的也就是主线程进行等待,等待其他的后台线程执行完毕,如果不进行等待的话,会导致最后的结果不准确,达不到我们所想要的结果
另一个方法就是使用线程池
public static void main(String[] args) {
Bank bank = new Bank();
ExecutorService executorService = Executors.newFixedThreadPool(10000);
for(int i =0;i<10000;i++) {
executorService.submit(() -> {
bank.deposit(100);
});
}
for(int i =0;i<5000;i++) {
executorService.submit(() -> {
bank.withdraw(100);
});
}
executorService.shutdown();
try{
//这里两个参数,一个是等待的时间,还有一个是的等待时间的单位
boolean awaited = executorService.awaitTermination(5, TimeUnit.SECONDS);
System.out.println("等待的输出信息为:"+awaited);
} catch (InterruptedException e) {
System.out.println("异常的错误信息:"+e.getMessage());
}
System.out.println(bank.getBalance());
}
线程池的shutdown方法用于关闭线程池,停止接受新的任务,并允许已有的任务继续执行直到完成。它不会立即终止正在执行的任务,而是等待所有已提交的任务完成。
这里只是是实现了一个存取款的功能
接下来加上了一个转账的功能
public class BankAccount {
//这个实现一个转账的功能
private String name;
private double balance;
//进行初始化
public BankAccount(String name, double balance) {
this.name = name;
this.balance = balance;
}
//这里要指定是哪一个账户
public synchronized void deposit( double amount) {
balance +=amount;
}
public synchronized void withdraw(double amount) {
if(balance >= amount) {
//进行取款
System.out.println("可以进行取款");
balance = balance - amount;
} else {
System.out.println("不能进行取款,取款金额大于存款");
balance = 0;
return ;
}
}
public synchronized void transferMoney(BankAccount target, double amount) {
if(target.balance >= amount) {
balance-=amount;
target.balance += amount;
System.out.println(name+"向"+target.name+"转账了"+amount+"钱");
} else{
System.out.println("存款不够,不支持转账");
return ;
}
}
public double getBalance() {
return balance;
}
}
public static void main3(String[] args) {
BankAccount bankAccount1=new BankAccount("zhangsan",10000);
BankAccount bankAccount2=new BankAccount("lisi",5000);
BankAccount bankAccount3=new BankAccount("wangwu",1000);
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> bankAccount1.transferMoney(bankAccount2, 2000));
executor.submit(() -> bankAccount1.transferMoney(bankAccount3, 1500));//6500 7000 2500
executor.submit(() -> bankAccount2.transferMoney(bankAccount3, 1000));//6500 6000 3500
executor.submit(() -> bankAccount3.transferMoney(bankAccount1, 500));//7000 6000 3000
executor.submit(() -> bankAccount2.transferMoney(bankAccount1, 750));//7750 5250 3000
executor.submit(() -> bankAccount1.deposit(3000));
executor.submit(() -> bankAccount2.withdraw(1000));
executor.submit(() -> bankAccount3.deposit(2000));// 10750 4250 5000
executor.shutdown();
try{
executor.awaitTermination(1,TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("\n最终余额:");
System.out.println("A1001: " + bankAccount1.getBalance());
System.out.println("A1002: " + bankAccount2.getBalance());
System.out.println("A1003: " + bankAccount3.getBalance());