【多线程】存取款问题

实现一个典型的存取款问题

两种实现方式

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类来创建线程。

【多线程】存取款问题_第1张图片

这里为什么要进行等待呢

是因为我们这个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());

你可能感兴趣的:(【Java-EE初阶】,java,开发语言)