Java使用鎖解決銀行取錢問(wèn)題實(shí)例分析
本文實(shí)例講述了Java使用鎖解決銀行取錢問(wèn)題。分享給大家供大家參考,具體如下:
一 點(diǎn)睛
1 釋放同步監(jiān)視器
線程會(huì)在如下幾種情況下釋放對(duì)同步監(jiān)視器的鎖定:
- 當(dāng)前線程的同步方法、同步代碼塊執(zhí)行結(jié)束,當(dāng)前線程即釋放同步監(jiān)視器。
- 當(dāng)線程在同步代碼塊、同步方法中遇到break、return終止了該代碼塊、該方法的繼續(xù)執(zhí)行,當(dāng)前線程將會(huì)釋放同步監(jiān)視器。
- 當(dāng)線程在同步代碼塊、同步方法中出現(xiàn)了未處理的Error或Exception,導(dǎo)致了該代碼塊、該方法異常結(jié)束時(shí)將會(huì)釋放同步監(jiān)視器。
- 當(dāng)線程執(zhí)行同步代碼塊或同步方法時(shí),程序執(zhí)行了同步監(jiān)視器對(duì)象的wait()方法,則當(dāng)前線程暫停,并釋放同步監(jiān)視器。
2 同步鎖
Lock是控制多個(gè)線程對(duì)共享資源進(jìn)行訪問(wèn)的工具。
通常,鎖提供了對(duì)共享資源的獨(dú)占訪問(wèn),每次只能有一個(gè)線程對(duì)Lock對(duì)象加鎖,線程開始訪問(wèn)共享資源之前應(yīng)先獲得Lock對(duì)象。
不過(guò),某些鎖可能允許對(duì)共享資源并發(fā)訪問(wèn),如 ReadWriteLock(讀寫鎖)。當(dāng)然,在實(shí)現(xiàn)線程安全的控制中,通常喜歡使用ReentrantLock(可重入鎖)。使用該Lock對(duì)象可以顯式地加鎖、釋放鎖。
ReentrantLock鎖具有可重入性,也就是說(shuō)線程可以對(duì)已加鎖的ReentrantLock鎖再次加鎖,ReentrantLock對(duì)象會(huì)維持一個(gè)計(jì)數(shù)器來(lái)追蹤lock方法的嵌套調(diào)用,線程在每次調(diào)用lock()方法加鎖后,必須顯式調(diào)用unlock()方法來(lái)釋放鎖,所以一段被鎖保護(hù)的代碼可以調(diào)用另一個(gè)被相同鎖保護(hù)的方法。
二 代碼
1 定義一個(gè)賬戶類
import java.util.concurrent.locks.*;
public class Account
{
// 定義鎖對(duì)象
private final ReentrantLock lock = new ReentrantLock();
// 封裝賬戶編號(hào)、賬戶余額的兩個(gè)成員變量
private String accountNo;
private double balance;
public Account(){}
// 構(gòu)造器
public Account(String accountNo , double balance)
{
this.accountNo = accountNo;
this.balance = balance;
}
// accountNo的setter和getter方法
public void setAccountNo(String accountNo)
{
this.accountNo = accountNo;
}
public String getAccountNo()
{
return this.accountNo;
}
// 因此賬戶余額不允許隨便修改,所以只為balance提供getter方法,
public double getBalance()
{
return this.balance;
}
// 提供一個(gè)線程安全draw()方法來(lái)完成取錢操作
public void draw(double drawAmount)
{
// 加鎖
lock.lock();
try
{
// 賬戶余額大于取錢數(shù)目
if (balance >= drawAmount)
{
// 吐出鈔票
System.out.println(Thread.currentThread().getName()
+ "取錢成功!吐出鈔票:" + drawAmount);
try
{
Thread.sleep(1);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
// 修改余額
balance -= drawAmount;
System.out.println("\t余額為: " + balance);
}
else
{
System.out.println(Thread.currentThread().getName()
+ "取錢失敗!余額不足!");
}
}
finally
{
// 修改完成,釋放鎖
lock.unlock();
}
}
// 下面兩個(gè)方法根據(jù)accountNo來(lái)重寫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è)取錢線程
public class DrawThread extends Thread
{
// 模擬用戶賬戶
private Account account;
// 當(dāng)前取錢線程所希望取的錢數(shù)
private double drawAmount;
public DrawThread(String name , Account account
, double drawAmount)
{
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
// 當(dāng)多條線程修改同一個(gè)共享數(shù)據(jù)時(shí),將涉及數(shù)據(jù)安全問(wèn)題。
public void run()
{
// 直接調(diào)用account對(duì)象的draw方法來(lái)執(zhí)行取錢
// 同步方法的同步監(jiān)視器是this,this代表調(diào)用draw()方法的對(duì)象。
// 也就是說(shuō):線程進(jìn)入draw()方法之前,必須先對(duì)account對(duì)象的加鎖。
account.draw(drawAmount);
}
}
3 測(cè)試主類
public class DrawTest
{
public static void main(String[] args)
{
// 創(chuàng)建一個(gè)賬戶
Account acct = new Account("1234567" , 1000);
// 模擬兩個(gè)線程對(duì)同一個(gè)賬戶取錢
new DrawThread("甲" , acct , 800).start();
new DrawThread("乙" , acct , 800).start();
}
}
三 運(yùn)行
乙取錢成功!吐出鈔票:800.0
余額為: 200.0
甲取錢失??!余額不足!
四 說(shuō)明
Account類中定義了一個(gè)ReentrantLock對(duì)象,程序?qū)崿F(xiàn)draw()方法時(shí),進(jìn)入方法開始執(zhí)行后立即請(qǐng)求對(duì)ReentrantLock對(duì)象進(jìn)行加鎖,當(dāng)執(zhí)行完draw()方法的取錢邏輯之后,程序使用finally塊來(lái)確保釋放鎖。
更多java相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Java進(jìn)程與線程操作技巧總結(jié)》、《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對(duì)大家java程序設(shè)計(jì)有所幫助。
- Java銀行取錢線程安全問(wèn)題實(shí)例分析
- Java同步代碼塊解決銀行取錢的安全問(wèn)題實(shí)例分析
- 以銀行取錢為例模擬Java多線程同步問(wèn)題完整代碼
- Java 模擬銀行自助終端系統(tǒng)
- Java基于JDBC實(shí)現(xiàn)事務(wù),銀行轉(zhuǎn)賬及貨物進(jìn)出庫(kù)功能示例
- 完美解決Java中的線程安全問(wèn)題
- 實(shí)例解析Java中的synchronized關(guān)鍵字與線程安全問(wèn)題
- Java多線程模擬售票程序和線程安全問(wèn)題
- 基于java線程安全問(wèn)題及原理性分析
- Java使用同步方法解決銀行取錢的安全問(wèn)題案例分析
相關(guān)文章
實(shí)現(xiàn)一個(gè)規(guī)則引擎的可視化具體方案
項(xiàng)目原因需要用到規(guī)則引擎,但是發(fā)現(xiàn)大部分不可以自由的進(jìn)行規(guī)則定義,通過(guò)不斷嘗試變換關(guān)鍵字在搜索引擎搜索,最終在stackoverflow找到了一個(gè)探討這個(gè)問(wèn)題的帖子,特此將帖子中提到的方案分享一下,如果你跟我一樣在研究同樣的問(wèn)題,也許對(duì)你有用2021-04-04
SpringBoot指標(biāo)監(jiān)控功能實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot指標(biāo)監(jiān)控功能實(shí)現(xiàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06
詳解Java消息隊(duì)列-Spring整合ActiveMq
本篇文章主要介紹了詳解Java消息隊(duì)列-Spring整合ActiveMq ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02
Mybatis關(guān)聯(lián)查詢結(jié)果集對(duì)象嵌套的具體使用
在查詢時(shí)經(jīng)常出現(xiàn)一對(duì)多”的關(guān)系,所有會(huì)出現(xiàn)嵌套對(duì)象的情況,本文主要介紹了Mybatis關(guān)聯(lián)查詢結(jié)果集對(duì)象嵌套的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
Spring Cloud微服務(wù)架構(gòu)的構(gòu)建:分布式配置中心(加密解密功能)
這篇文章主要給大家介紹了關(guān)于Spring Cloud微服務(wù)架構(gòu)的構(gòu)建:分布式配置中心(加密解密)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2018-05-05
Idea如何使用System.getenv()獲取環(huán)境變量的值
文章介紹了如何在Java中使用`System.getenv()`方法讀取環(huán)境變量的值,并提供了兩種配置環(huán)境變量的方法:?jiǎn)?dòng)項(xiàng)配置和系統(tǒng)環(huán)境變量配置,對(duì)于系統(tǒng)環(huán)境變量,文章特別指出需要重啟電腦或程序才能使其生效2024-11-11

