Java同步代碼塊解決銀行取錢(qián)的安全問(wèn)題實(shí)例分析
本文實(shí)例講述了Java同步代碼塊解決銀行取錢(qián)的安全問(wèn)題。分享給大家供大家參考,具體如下:
一 點(diǎn)睛
為了解決類(lèi)似銀行取錢(qián)這類(lèi)安全問(wèn)題,Java的多線(xiàn)程支持引入了同步監(jiān)視器來(lái)解決這個(gè)問(wèn)題,使用同步監(jiān)視器的通用方法是同步代碼塊。同步代碼塊的語(yǔ)法格式是:
synchronized(obj) { //此處代碼塊就是同步代碼塊。 }
上面語(yǔ)法格式中種的obj就是同步監(jiān)視器,上面代碼的含義是:線(xiàn)程開(kāi)始執(zhí)行同步代碼塊之前,必須先獲得對(duì)同步監(jiān)視器的鎖定。
任何時(shí)刻只能由一個(gè)線(xiàn)程獲得對(duì)同步監(jiān)視器的鎖定,當(dāng)同步代碼塊執(zhí)行完成后,該線(xiàn)程會(huì)釋放對(duì)該同步監(jiān)視器的鎖定。
雖然Java程序允許使用任何對(duì)象作為同步監(jiān)視器,但想一下同步監(jiān)視器的目的:阻止兩個(gè)線(xiàn)程對(duì)同一共享資源進(jìn)行并發(fā)訪(fǎng)問(wèn),因此通常推薦使用可能被并發(fā)訪(fǎng)問(wèn)的共享資源作為同步監(jiān)視器。
二 代碼
1 定義一個(gè)賬戶(hù)類(lèi)
public class Account { // 封裝賬戶(hù)編號(hào)、賬戶(hù)余額的兩個(gè)成員變量 private String accountNo; private double balance; public Account(){} // 構(gòu)造器 public Account(String accountNo , double balance) { this.accountNo = accountNo; this.balance = balance; } // 此處省略了accountNo和balance的setter和getter方法 // accountNo的setter和getter方法 public void setAccountNo(String accountNo) { this.accountNo = accountNo; } public String getAccountNo() { return this.accountNo; } // balance的setter和getter方法 public void setBalance(double balance) { this.balance = balance; } public double getBalance() { return this.balance; } // 下面兩個(gè)方法根據(jù)accountNo來(lái)重寫(xiě)hashCode()和equals()方法 public int hashCode() { return accountNo.hashCode(); } public boolean equals(Object obj) { if(this == obj) return true; if (obj !=null && obj.getClass() == Account.class) { Account target = (Account)obj; return target.getAccountNo().equals(accountNo); } return false; } }
2 定義一個(gè)取錢(qián)線(xiàn)程
public class DrawThread extends Thread { // 模擬用戶(hù)賬戶(hù) private Account account; // 當(dāng)前取錢(qián)線(xiàn)程所希望取的錢(qián)數(shù) private double drawAmount; public DrawThread(String name, Account account, double drawAmount) { super(name); this.account = account; this.drawAmount = drawAmount; } // 當(dāng)多條線(xiàn)程修改同一個(gè)共享數(shù)據(jù)時(shí),將涉及數(shù)據(jù)安全問(wèn)題。 public void run() { synchronized (account) { // 賬戶(hù)余額大于取錢(qián)數(shù)目 if (account.getBalance() >= drawAmount) { // 吐出鈔票 System.out.println(getName() + "取錢(qián)成功!吐出鈔票:" + drawAmount); try { Thread.sleep(1); } catch (InterruptedException ex) { ex.printStackTrace(); } // 修改余額 account.setBalance(account.getBalance() - drawAmount); System.out.println("\t余額為: " + account.getBalance()); } else { System.out.println(getName() + "取錢(qián)失??!余額不足!"); } } } }
3 測(cè)試主類(lèi)
public class DrawTest { public static void main(String[] args) { // 創(chuàng)建一個(gè)賬戶(hù) Account acct = new Account("1234567" , 1000); // 模擬兩個(gè)線(xiàn)程對(duì)同一個(gè)賬戶(hù)取錢(qián) new DrawThread("甲" , acct , 800).start(); new DrawThread("乙" , acct , 800).start(); } }
三 運(yùn)行
甲取錢(qián)成功!吐出鈔票:800.0
余額為: 200.0
乙取錢(qián)失??!余額不足!
四 說(shuō)明
使用synchronized將run()方法里的方法體修改為同步代碼塊,該同步代碼塊的同步監(jiān)視器是account對(duì)象,這樣的做法符合“加鎖->修改->釋放鎖”的邏輯,任何線(xiàn)程在修改指定資源之前,首先對(duì)該資源加鎖,在加鎖期間其他線(xiàn)程無(wú)法修改該資源,當(dāng)該線(xiàn)程修改完成后,該線(xiàn)程釋放對(duì)該資源的鎖定。通過(guò)這種方式就可以保證并發(fā)線(xiàn)程任一時(shí)刻只有一個(gè)線(xiàn)程可以進(jìn)入修改共享資源的代碼區(qū)(也稱(chēng)臨界區(qū)),所以同一時(shí)刻最多只有一個(gè)線(xiàn)程處于臨界區(qū),從而保證線(xiàn)程的安全。
更多java相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《Java面向?qū)ο蟪绦蛟O(shè)計(jì)入門(mén)與進(jìn)階教程》、《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對(duì)大家java程序設(shè)計(jì)有所幫助。
相關(guān)文章
IDEA 集成log4j將SQL語(yǔ)句打印在控制臺(tái)上的實(shí)現(xiàn)操作
這篇文章主要介紹了IDEA 集成log4j將SQL語(yǔ)句打印在控制臺(tái)上的實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02Springboot 2.x中server.servlet.context-path的運(yùn)用詳解
這篇文章主要介紹了Springboot 2.x中server.servlet.context-path的運(yùn)用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01Spring中的三級(jí)緩存與循環(huán)依賴(lài)詳解
Spring三級(jí)緩存是Spring框架中用于解決循環(huán)依賴(lài)問(wèn)題的一種機(jī)制,這篇文章主要介紹了Spring三級(jí)緩存與循環(huán)依賴(lài)的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-05-05Java實(shí)現(xiàn)簡(jiǎn)單日歷小程序 Java圖形界面小日歷開(kāi)發(fā)
這篇文章主要介紹了Java實(shí)現(xiàn)簡(jiǎn)單日歷小程序,如何用Java swing開(kāi)發(fā)一款簡(jiǎn)單的小日歷,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02解決Spring Mvc中對(duì)象綁定參數(shù)重名的問(wèn)題
最近在工作中遇到了參數(shù)綁定的一個(gè)問(wèn)題,發(fā)現(xiàn)網(wǎng)上這方面的資料較少,索性自己來(lái)總結(jié)下,下面這篇文章主要給大家介紹了關(guān)于如何解決Spring Mvc中對(duì)象綁定參數(shù)重名問(wèn)題的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-08-08Springboot工具類(lèi)ReflectionUtils使用教程
這篇文章主要介紹了Springboot內(nèi)置的工具類(lèi)之ReflectionUtils的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-12-12