模拟银行账户

很重要,对于理解 锁的是哪个对象很重要以及volatile的可见性并不能代表数据的一致性以及数据的原子性,因此volatile并非是数据安全的。

模拟银行账户读写,数据是否一致

public class Account_01 {

    private String name;
    private int balance;

    void set(String name, int balance) {
        // 此处睡两秒是为了保证 main 主线程先执行一次getBalance方法
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.name = name;
        this.balance = balance;
    }

    int getBalance(String name) {
        return balance;
    }

    public static void main(String[] args) throws InterruptedException {
        Account_01 a = new Account_01();
        new Thread(() -> a.set("zhangsan", 2), "t1").start();

        System.out.println("thread t1 exe set before balance value :  " + a.getBalance("zhangsan"));

        // 此处睡3 秒是为了保证线程 t1 set 方法 执行完毕
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread t1 exe set after balance value:  " + a.getBalance("zhangsan"));
    }
}
输出结果:
thread t1 exe set before balance value :  0
thread t1 exe set after balance value:  2
    

 出现了脏读现象,现在为了保持 写入的数据和读到的数据保持一致,代码做以下更改。

加synchronized

public class Account_01 {

    private String name;
    private int balance;

    synchronized void set(String name, int balance) {
        // 此处睡两秒是为了保证 main 主线程先执行一次getBalance方法
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.name = name;
        this.balance = balance;
    }

    synchronized int getBalance(String name) {
        return balance;
    }

    public static void main(String[] args) throws InterruptedException {
        Account_01 a = new Account_01();
        new Thread(() -> a.set("zhangsan", 2), "t1").start();

        System.out.println("thread t1 exe set before balance value :  " + a.getBalance("zhangsan"));

        // 此处睡3 秒是为了保证线程 t1 set 方法 执行完毕
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread t1 exe set after balance value:  " + a.getBalance("zhangsan"));


    }
}

输出结果:
thread t1 exe set before balance value :  2
thread t1 exe set after balance value:  2

读和写结果是一致的,此处说明了 synchronized 锁定的是当前对象,当 我们去调用get 方法时提示有锁,需要等待set方法执行完毕之后才可以执行 get方法。 重点,方法上的锁是对象的。

为啥不用volatile 呢, 试试看下输出结果。

public class Account_01 {

    private String name;
    private volatile int balance;

    synchronized void set(String name, int balance) {
        // 此处睡两秒是为了保证 main 主线程先执行一次getBalance方法
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.name = name;
        this.balance = balance;
    }

     int getBalance(String name) {
        return balance;
    }

    public static void main(String[] args) throws InterruptedException {
        Account_01 a = new Account_01();
        new Thread(() -> a.set("zhangsan", 2), "t1").start();

        System.out.println("thread t1 exe set before balance value :  " + a.getBalance("zhangsan"));

        // 此处睡3 秒是为了保证线程 t1 set 方法 执行完毕
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread t1 exe set after balance value:  " + a.getBalance("zhangsan"));


    }
}

输出结果:
thread t1 exe set before balance value :  0
thread t1 exe set after balance value:  2

输出结果 是读和写不一致,再一次说明了 volatile 只能保持数据的可见性,并不能说明数据的一致性,synchronized 才可以保证数据的原子性和一致性。

用了 synchronized 之后还需要用volatile 吗? 不需要。synchronized 已经保证了多线程之间数据的一致性了。

模拟多线程写,用map存

public class Account_01 {

    private String name;
    private volatile int balance;

    private Map<String, Integer> map = new HashMap<>();

    synchronized void set(String name, int balance) {
        balance = (Objects.isNull(map.get(name)) ? 0 : map.get(name)) + balance;
        map.put(name, balance);

    }

    synchronized int getBalance(String name) {
        return (Objects.isNull(map.get(name)) ? 0 : map.get(name));
    }

    public static void main(String[] args) throws InterruptedException {
        Account_01 a = new Account_01();

        CountDownLatch start = new CountDownLatch(1);
        CountDownLatch end = new CountDownLatch(20);
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                try {
                    start.await();
                    a.set("zhangsan", 2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    end.countDown();
                }
            }, "t1").start();
        }

        start.countDown();
        end.await();
        System.out.println("输出结果: " + a.getBalance("zhangsan"));

    }
}

输出结果:
输出结果:  40  // get 方法加锁了,等到 set 完成释放锁之后才拿到锁执行get 方法获取到值

是否为同一把锁

@SuppressWarnings("all")
public class Account_01 {

    private String name;
    private volatile int balance;

    private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

    void set(String name, int balance) {
        synchronized (this) {
            balance = (Objects.isNull(map.get(name)) ? 0 : map.get(name)) + balance;
        }
        map.put(name, balance);
    }

    synchronized int getBalance(String name) {
        return (Objects.isNull(map.get(name)) ? 0 : map.get(name));
    }

    public static void main(String[] args) throws InterruptedException {
        for (int n = 0; n < 500000; n++) {
            Account_01 a = new Account_01();
            CountDownLatch start = new CountDownLatch(1);
            CountDownLatch end = new CountDownLatch(20);
            for (int i = 0; i < 20; i++) {
                new Thread(() -> {
                    try {
                        start.await();
                        a.set("zhangsan", 2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        end.countDown();
                    }
                }, "t1").start();
            }

            start.countDown();
            end.await();
            if (a.getBalance("zhangsan") != 40) {
                System.out.println("Incorrect result output: " + a.getBalance("zhangsan"));
            }
            System.out.println("Correct result output: " + a.getBalance("zhangsan"));
        }
    }
}

输出结果:
Correct result output: 40
Correct result output: 40
Incorrect result output: 38
Correct result output: 38
Correct result output: 40
Correct result output: 40
Incorrect result output: 38
Correct result output: 38
Correct result output: 40
Correct result output: 40
Correct result output: 40
Correct result output: 40
Correct result output: 40
Correct result output: 40
Correct result output: 40
Correct result output: 40‘

很显然输出结果里面有读写不一致的情况。这个代码里面我们用到了ConcurrentHashMap 为啥还读写不一致呢,个人认为 读和写并不是同一把锁导致的。所以要体现出读写一致,必定需要使用同一把锁
好的,我可以为您提供一个简单的Java模拟银行账户管理系统的示例。 首先,我们需要定义一个账户类,包括账户名、账户余额等属性和相关的方法,如存款、取款等。 ```java public class Account { private String name; private double balance; public Account(String name, double balance) { this.name = name; this.balance = balance; } public String getName() { return name; } public double getBalance() { return balance; } public void deposit(double amount) { balance += amount; } public void withdraw(double amount) { if (amount <= balance) { balance -= amount; } else { System.out.println("余额不足!"); } } } ``` 接下来,我们可以定义一个银行类,包含多个账户和相关的操作方法,如开户、销户、查询余额等。 ```java import java.util.ArrayList; public class Bank { private ArrayList<Account> accounts; public Bank() { accounts = new ArrayList<>(); } public void addAccount(Account account) { accounts.add(account); } public void removeAccount(Account account) { accounts.remove(account); } public double getBalance(String name) { for (Account account : accounts) { if (account.getName().equals(name)) { return account.getBalance(); } } System.out.println("账户不存在!"); return -1; } public void deposit(String name, double amount) { for (Account account : accounts) { if (account.getName().equals(name)) { account.deposit(amount); System.out.println("存款成功!"); return; } } System.out.println("账户不存在!"); } public void withdraw(String name, double amount) { for (Account account : accounts) { if (account.getName().equals(name)) { account.withdraw(amount); System.out.println("取款成功!"); return; } } System.out.println("账户不存在!"); } } ``` 最后,我们可以编写一个测试类,模拟用户使用银行账户管理系统的过程。 ```java import java.util.Scanner; public class TestBank { public static void main(String[] args) { Bank bank = new Bank(); // 开户 Account account1 = new Account("张三", 1000); Account account2 = new Account("李四", 2000); bank.addAccount(account1); bank.addAccount(account2); Scanner scanner = new Scanner(System.in); while (true) { System.out.println("请选择操作:1.查询余额 2.存款 3.取款 4.退出"); int option = scanner.nextInt(); if (option == 1) { System.out.println("请输入账户名:"); String name = scanner.next(); double balance = bank.getBalance(name); if (balance >= 0) { System.out.println("余额为:" + balance); } } else if (option == 2) { System.out.println("请输入账户名和存款金额:"); String name = scanner.next(); double amount = scanner.nextDouble(); bank.deposit(name, amount); } else if (option == 3) { System.out.println("请输入账户名和取款金额:"); String name = scanner.next(); double amount = scanner.nextDouble(); bank.withdraw(name, amount); } else if (option == 4) { break; } else { System.out.println("输入错误,请重新选择!"); } } } } ``` 以上就是一个简单的Java模拟银行账户管理系统的示例,您可以参考并根据自己的需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值