Java线程安全和线程同步(银行取钱案例)

一、线程安全问题

  • 案例:银行多用户操作同一账户

    public class SafeBank {public static void main(String[] args) {Account account = new Account("建行卡", 100);new Thread(new Bank(account,50),"小明").start();new Thread(new Bank(account,100),"小明老婆").start();}
    }class Account {String name;int money;public Account(String name, int money) {this.name = name;this.money = money;}
    }class Bank implements Runnable{Account account;int needMoney;public Bank(Account account, int needMoney) {this.account = account;this.needMoney = needMoney;}@Overridepublic void run() {getMoney();}public void getMoney() {if (account.money <= 0) {System.out.println("余额不足");return;}if (needMoney > account.money) {System.out.println(Thread.currentThread().getName() + "想取钱,但余额不足");return;}System.out.println(Thread.currentThread().getName() + "取了" + needMoney + "万");account.money -= needMoney;System.out.println("账户剩余" + account.money + "万");}
    }
    
  • 运行结果

  • 为什么会这样?

    代码逻辑无问题,但由于同时开了两个account目标对象的线程,存在线程安全问题。要解决这个问题,需要用到线程同步

二、线程同步

  • 同步方法

    public class SafeBank {public static void main(String[] args) {Account account = new Account("建行卡", 100);new Thread(new Bank(account,50),"小明").start();new Thread(new Bank(account,100),"小明老婆").start();}
    }class Account {String name;int money;public Account(String name, int money) {this.name = name;this.money = money;}
    }class Bank implements Runnable{Account account;int needMoney;public Bank(Account account, int needMoney) {this.account = account;this.needMoney = needMoney;}@Overridepublic void run() {getMoney();}public synchronized void getMoney() {if (account.money <= 0) {System.out.println("余额不足");return;}if (needMoney > account.money) {System.out.println(Thread.currentThread().getName() + "想取钱,但余额不足");return;}System.out.println(Thread.currentThread().getName() + "取了" + needMoney + "万");account.money -= needMoney;System.out.println("账户剩余" + account.money + "万");}
    }
    

    运行结果:

    ​ 由运行结果可以看出,还是没解决线程安全问题,为什么?

​ 因为同步方法,默认锁的对象时this,即本类,在此案例中,this代指Bank,而发生变化的数据是account对象的,因此synchronized关键字并没有锁住该目标对象,因此运行结果仍然存在线程安全问题,怎么解决呢,这里可以只用同步块的方式来解决

  • 同步块

    • 同步块 : synchronized (Obj ) { }
    • Obj :同步监视器 , Obj 可以是任何对象 , 但是推荐使用共享资源作为同步监视器 , 同步方法中无需指定同步监视器 , 因为同步方法的同步监视器就是this , 就是 这个对象本身 , 或者是 class
    • 同步监视器的执行过程
      1. 第一个线程访问 , 锁定同步监视器 , 执行其中代码 .
      2. 第二个线程访问 , 发现同步监视器被锁定 , 无法访问 .
      3. 第一个线程访问完毕 , 解锁同步监视器 .
      4. 第二个线程访问, 发现同步监视器没有锁 , 然后锁定并访问
    public class SafeBank {public static void main(String[] args) {Account account = new Account("建行卡", 100);new Thread(new Bank(account,50),"小明").start();new Thread(new Bank(account,100),"小明老婆").start();}
    }class Account {String name;int money;public Account(String name, int money) {this.name = name;this.money = money;}
    }class Bank implements Runnable{Account account;int needMoney;public Bank(Account account, int needMoney) {this.account = account;this.needMoney = needMoney;}@Overridepublic void run() {getMoney();}public void getMoney() {synchronized (account) {if (account.money <= 0) {System.out.println("余额不足");return;}if (needMoney > account.money) {System.out.println(Thread.currentThread().getName() + "想取钱,但余额不足");return;}System.out.println(Thread.currentThread().getName() + "取了" + needMoney + "万");account.money -= needMoney;System.out.println("账户剩余" + account.money + "万");}}
    }
    

    运行结果:

    由运行结果可以看出线程安全问题已经解决。

  • 同步锁

    • 从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步,同步锁由Lock对象充当。比较常用的是ReentrantLock(可重入锁)。

    • 代码格式

    public class X {//定义锁对象private final ReentrantLock lock = new ReentrantLock();//定义需要保证线程安全的方法public void m(){//加锁lock.lock();try {//需要保证线程安全的代码}finally {//使用finally块来保证释放锁lock.unlock();}}
    }
    
    import java.util.concurrent.locks.ReentrantLock;public class SafeBank {public static void main(String[] args) {Account account = new Account("建行卡", 100);new Thread(new Bank(account, 50), "小明").start();new Thread(new Bank(account, 100), "小明老婆").start();}
    }class Account {String name;int money;public Account(String name, int money) {this.name = name;this.money = money;}
    }class Bank implements Runnable {private final ReentrantLock lock = new ReentrantLock();Account account;int needMoney;public Bank(Account account, int needMoney) {this.account = account;this.needMoney = needMoney;}@Overridepublic void run() {getMoney();}public void getMoney() {lock.lock();try {if (account.money <= 0) {System.out.println("余额不足");return;}if (needMoney > account.money) {System.out.println(Thread.currentThread().getName() + "想取钱,但余额不足");return;}System.out.println(Thread.currentThread().getName() + "取了" + needMoney + "万");account.money -= needMoney;System.out.println("账户剩余" + account.money + "万");} finally {lock.unlock();}}}
    

    运行结果:

    ​ 从运行结果可以看出,并未能解决线程同步问题,为什么?

​ 使用同步锁与同步方法有点类似,只是使用Lock时显式使用Lock对象作为同步锁,而使用同步方法时系统隐式使用当前对象作为同步监视器。在此处,虽然显式使用了Lock,但锁住的对象仍是Bank,而不是account,因此并没有解决线程安全问题,想要解决此问题,可以将Bank类的方法功能整合到Account类中,然后使用Lock显式调用锁住account。

Java线程安全和线程同步——以银行取钱为例相关推荐

  1. 线程同步之模拟银行取钱实例

      线程同步的机制就是队列和锁,把共享的资源加上一把锁,然后把线程进行排队,实现安全的线程.下面为大家模拟银行取钱的实例,代码如下: public class Unsafebank {public s ...

  2. java银行取款_Java写简单的银行取钱系统

    按ctrl+a 选中所有的代码删除,再复制下面的代码输入即可 import java.util.Scanner; public class 简单的银行取钱系统 //class的名字可以自己新建和这个一 ...

  3. java多线程学习二、安全与不安全示例:12306买票和银行取钱、java内存模型、内存可见性、线程同步块和方法

    文章目录 前言 1. 什么是块,分为几种 2. 静态块与构造块的区别 一. 举例说明:并发情况下,线程不安全 1. 示例1:unsafe12306取票 2. 示例2:unsafe银行取钱 二.线程不安 ...

  4. 线程安全经典案例:银行取钱问题

    银行取钱的基本流程基本上可以分为如下几个步骤. (1)用户输入账户.密码,系统判断用户的账户.密码是否匹配. (2)用户输入取款密码 (3)系统判断账户余额是否大于取款余额 (4)如果余额大于取款余额 ...

  5. java银行安全性_Java使用同步方法解决银行取钱的安全问题案例分析

    本文实例讲述了Java使用同步方法解决银行取钱的安全问题.分享给大家供大家参考,具体如下: 一 点睛 与同步代码块对应,Java的多线程安全支持还提供了同步方法,同步方法就是使用synchronize ...

  6. 经典的同步问题(银行取钱)

    银行取钱问题是非常经典的同步问题,如果不采用同步方法,可能也不会发生错误,但就是那小概率事件就可以称之为BUG吧. 没有使用同步方法的代码如下: public class ErrorTest{publ ...

  7. java多线程学习一、线程介绍、线程创建的3种方式、lambda创建方式、线程状态、线程示例:12306买票和银行取钱

    文章目录 前言 一.线程简介 1.概述 2.进程.线程 区别 在这里插入图片描述 3. 核心概念 二. 线程创建 1.概述 2. 第一种方式继承Thread 1) 继承Thread 2) 示例:下载图 ...

  8. 以同一张银行卡取钱为例,演示同步多线程方法

    public class BankDemo { public static void main(String[] args) { Bank bank=new Bank(); BankThread p1 ...

  9. java多线程银行取钱_用java线程同步实现银行取款和存款。

    题目是:编写程序实现线程的同步.假设一个银行的ATM机,它可以允许用户存款也可以取款.现在一个账户上有200元,用户A和用户B都拥有在这个账户上存款和取款的权利.用户A将存入100元... 题目是:编 ...

最新文章

  1. CoreCRM 开发实录 —— Profile
  2. jQuery如何得到tagName?
  3. MIT:睡眠不足会让你连路都走不好,但补觉还有救
  4. 基础中的基础。CANVAS step01
  5. 《结对-结对编项目作业名称-开发过程》
  6. 【Linux】- 获取root权限命令
  7. python如何使用session和cookie_Python爬虫番外篇之Cookie和Session-阿里云开发者社区
  8. python数据类型汇总_python基础数据类型汇总
  9. MySQL 数据还原
  10. 【分享】一套非常简单的企业即时通讯
  11. Vue中子组件向父组件请求数据时的数据格式问题
  12. 晚间看图片就高亮,这体验太差
  13. 5分钟搞懂如何在Spring Boot中Schedule Tasks
  14. C语言——设置flag的优点
  15. ftp文件上传及下载工具类
  16. 大数据基础课第一课 Hadoop详解
  17. android百度地图公交路线,android百度地图api实现查询经过某站点的所有公交路线...
  18. 【系统运维-raid5】HW5885V3下挂4块2T硬盘如何做RAID5
  19. 获取mp3部分信息的python代码
  20. 【开发经验】md自动上传图片

热门文章

  1. 进程的句柄,PID及线程
  2. 养生理疗馆有哪些服务项目
  3. 偷窥了阿里的图像搜索架构,干货分享给你!
  4. 手机网速突然变慢是什么原因
  5. 电脑开不了机,是怎么回事?
  6. windows 7 中无法打开阿里助手中各项网页的解决方法
  7. 关于封装的三个最伟大的段落
  8. 爸妈用这些雷人语录训过你吗?
  9. 联想笔记本电脑连接不到无线网络的解决办法
  10. 游戏是怎么赚钱的 - 聊聊挖坑