Java同步代码块解决银行取钱的安全问题实例分析
本文实例讲述了Java同步代码块解决银行取钱的安全问题。分享给大家供大家参考,具体如下:
一点睛
为了解决类似银行取钱这类安全问题,Java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法是同步代码块。同步代码块的语法格式是:
synchronized(obj) { //此处代码块就是同步代码块。 }
上面语法格式中种的obj就是同步监视器,上面代码的含义是:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。
任何时刻只能由一个线程获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对该同步监视器的锁定。
虽然Java程序允许使用任何对象作为同步监视器,但想一下同步监视器的目的:阻止两个线程对同一共享资源进行并发访问,因此通常推荐使用可能被并发访问的共享资源作为同步监视器。
二代码
1定义一个账户类
publicclassAccount { //封装账户编号、账户余额的两个成员变量 privateStringaccountNo; privatedoublebalance; publicAccount(){} //构造器 publicAccount(StringaccountNo,doublebalance) { this.accountNo=accountNo; this.balance=balance; } //此处省略了accountNo和balance的setter和getter方法 //accountNo的setter和getter方法 publicvoidsetAccountNo(StringaccountNo) { this.accountNo=accountNo; } publicStringgetAccountNo() { returnthis.accountNo; } //balance的setter和getter方法 publicvoidsetBalance(doublebalance) { this.balance=balance; } publicdoublegetBalance() { returnthis.balance; } //下面两个方法根据accountNo来重写hashCode()和equals()方法 publicinthashCode() { returnaccountNo.hashCode(); } publicbooleanequals(Objectobj) { if(this==obj) returntrue; if(obj!=null &&obj.getClass()==Account.class) { Accounttarget=(Account)obj; returntarget.getAccountNo().equals(accountNo); } returnfalse; } }
2定义一个取钱线程
publicclassDrawThreadextendsThread{ //模拟用户账户 privateAccountaccount; //当前取钱线程所希望取的钱数 privatedoubledrawAmount; publicDrawThread(Stringname,Accountaccount,doubledrawAmount){ super(name); this.account=account; this.drawAmount=drawAmount; } //当多条线程修改同一个共享数据时,将涉及数据安全问题。 publicvoidrun(){ synchronized(account){ //账户余额大于取钱数目 if(account.getBalance()>=drawAmount){ //吐出钞票 System.out.println(getName()+"取钱成功!吐出钞票:"+drawAmount); try{ Thread.sleep(1); }catch(InterruptedExceptionex){ ex.printStackTrace(); } //修改余额 account.setBalance(account.getBalance()-drawAmount); System.out.println("\t余额为:"+account.getBalance()); }else{ System.out.println(getName()+"取钱失败!余额不足!"); } } } }
3测试主类
publicclassDrawTest { publicstaticvoidmain(String[]args) { //创建一个账户 Accountacct=newAccount("1234567",1000); //模拟两个线程对同一个账户取钱 newDrawThread("甲",acct,800).start(); newDrawThread("乙",acct,800).start(); } }
三运行
甲取钱成功!吐出钞票:800.0
余额为:200.0
乙取钱失败!余额不足!
四说明
使用synchronized将run()方法里的方法体修改为同步代码块,该同步代码块的同步监视器是account对象,这样的做法符合“加锁->修改->释放锁”的逻辑,任何线程在修改指定资源之前,首先对该资源加锁,在加锁期间其他线程无法修改该资源,当该线程修改完成后,该线程释放对该资源的锁定。通过这种方式就可以保证并发线程任一时刻只有一个线程可以进入修改共享资源的代码区(也称临界区),所以同一时刻最多只有一个线程处于临界区,从而保证线程的安全。
更多java相关内容感兴趣的读者可查看本站专题:《Java面向对象程序设计入门与进阶教程》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》
希望本文所述对大家java程序设计有所帮助。