Java線程同步Lock同步鎖代碼示例
java線程同步原理
java會(huì)為每個(gè)object對(duì)象分配一個(gè)monitor,當(dāng)某個(gè)對(duì)象的同步方法(synchronizedmethods)被多個(gè)線程調(diào)用時(shí),該對(duì)象的monitor將負(fù)責(zé)處理這些訪問(wèn)的并發(fā)獨(dú)占要求。
當(dāng)一個(gè)線程調(diào)用一個(gè)對(duì)象的同步方法時(shí),JVM會(huì)檢查該對(duì)象的monitor。如果monitor沒(méi)有被占用,那么這個(gè)線程就得到了monitor的占有權(quán),可以繼續(xù)執(zhí)行該對(duì)象的同步方法;如果monitor被其他線程所占用,那么該線程將被掛起,直到monitor被釋放。
當(dāng)線程退出同步方法調(diào)用時(shí),該線程會(huì)釋放monitor,這將允許其他等待的線程獲得monitor以使對(duì)同步方法的調(diào)用執(zhí)行下去。
注意:Java對(duì)象的monitor機(jī)制和傳統(tǒng)的臨界檢查代碼區(qū)技術(shù)不一樣。java的一個(gè)同步方法并不意味著同時(shí)只有一個(gè)線程獨(dú)占執(zhí)行,但臨界檢查代碼區(qū)技術(shù)確實(shí)會(huì)保證同步方法在一個(gè)時(shí)刻只被一個(gè)線程獨(dú)占執(zhí)行。Java的monitor機(jī)制的準(zhǔn)確含義是:任何時(shí)刻,對(duì)一個(gè)指定object對(duì)象的某同步方法只能由一個(gè)線程來(lái)調(diào)用。
java對(duì)象的monitor是跟隨object實(shí)例來(lái)使用的,而不是跟隨程序代碼。兩個(gè)線程可以同時(shí)執(zhí)行相同的同步方法,比如:一個(gè)類的同步方法是xMethod(),有a,b兩個(gè)對(duì)象實(shí)例,一個(gè)線程執(zhí)行a.xMethod(),另一個(gè)線程執(zhí)行b.xMethod().互不沖突。
Lock-同步鎖
Lock是java5提供的一個(gè)強(qiáng)大的線程同步機(jī)制--通過(guò)顯示定義同步鎖對(duì)象來(lái)實(shí)現(xiàn)同步。Lock可以顯示的加鎖、解鎖。每次只能有一個(gè)線程對(duì)lock對(duì)象加鎖。
Lock有ReadLock、WriteLock、ReentrantLock(可重入鎖)
常用的就是ReentrantLock。代碼如下:
代碼邏輯:Account賬戶類,實(shí)現(xiàn)取錢的同步方法、DrawThread取錢的線程
Account:
package lock.reentrantlock2; import java.util.concurrent.locks.*; /** *賬戶類,需保持同步 */ public class Account { //定義鎖對(duì)象 private final ReentrantLock lock = new ReentrantLock(); private String accountNo; private double balance; public Account(){} public Account(String accountNo , double balance) { this.accountNo = accountNo; this.balance = balance; } public void setAccountNo(String accountNo) { this.accountNo = accountNo; } public String getAccountNo() { return this.accountNo; } public double getBalance() { return this.balance; } 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(); } } public int hashCode() { return accountNo.hashCode(); } public boolean equals(Object obj) { if (obj != null && obj.getClass() == Account.class) { Account target = (Account)obj; return target.getAccountNo().equals(accountNo); } return false; } }
DrawThread:
package lock.reentrantlock2; /** * 調(diào)用account取錢 * */ 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() { account.draw(drawAmount); } }
TestDraw:
package lock.reentrantlock2; /** */ public class TestDraw { 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)行結(jié)果:
甲取錢成功!吐出鈔票:800.0
余額為:200.0
乙取錢失?。∮囝~不足!
使用Lock同步與同步方法很相似,都是“加鎖--修改公共變量--釋放鎖”的模式,代碼很容易看懂。兩個(gè)線程對(duì)應(yīng)一個(gè)Account對(duì)象,保證了兩個(gè)線程對(duì)應(yīng)一個(gè)lock對(duì)象,保證了同一時(shí)刻只有一個(gè)線程進(jìn)入臨界區(qū)。Lock還包含太容易Lock(),以及試圖獲取可中斷鎖的lockInterruptibly(),獲取超時(shí)失效鎖的tryLock(long,TimeUnit)等方法。
ReentrantLock鎖具有可重入性可以對(duì)已被加鎖的ReentrantLock鎖再次加鎖,線程每次調(diào)用lock()加鎖后,必須顯示的調(diào)用unlock來(lái)釋放鎖,有幾個(gè)lock就對(duì)應(yīng)幾個(gè)unlock。還有把unlock放在finally代碼塊中,Lock在發(fā)生異常時(shí)也是不釋放鎖的,所以在finally中釋放更安全。
總結(jié)
以上就是本文關(guān)于Java線程同步Lock同步鎖代碼示例的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:
創(chuàng)建并運(yùn)行一個(gè)java線程方法介紹
Java編程之多線程死鎖與線程間通信簡(jiǎn)單實(shí)現(xiàn)代碼
Java多線程編程小實(shí)例模擬停車場(chǎng)系統(tǒng)
有什么問(wèn)題可以隨時(shí)留言,小編會(huì)及時(shí)回復(fù)大家的。感謝朋友們對(duì)本站的支持!
相關(guān)文章
關(guān)于SpringCloud的Bus消息總線圖文詳解
這篇文章主要介紹了關(guān)于SpringCloud的Bus消息總線圖文詳解,Spring Cloud Bus是用來(lái)將分布式系統(tǒng)的節(jié)點(diǎn)與輕量級(jí)消息系統(tǒng)鏈接起來(lái)的框架,它整合了Java的事件處理機(jī)制和消息中間件的功能,需要的朋友可以參考下2023-05-05java線程之Happens before規(guī)則案例詳解
這篇文章主要為大家介紹了java線程之Happens-before規(guī)則,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2022-08-08idea項(xiàng)目實(shí)現(xiàn)移除和添加git
本文指導(dǎo)讀者如何從官網(wǎng)下載并安裝Git,以及在IDEA中配置Git的詳細(xì)步驟,首先,用戶需訪問(wèn)Git官方網(wǎng)站下載適合自己操作系統(tǒng)的Git版本并完成安裝,接著,在IDEA中通過(guò)設(shè)置找到git.exe文件以配置Gi2024-10-10Kotlin + Spring Boot 請(qǐng)求參數(shù)驗(yàn)證的代碼實(shí)例
本篇文章主要介紹了Kotlin + Spring Boot 請(qǐng)求參數(shù)驗(yàn)證的代碼實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07spring-integration連接MQTT全過(guò)程
這篇文章主要介紹了spring-integration連接MQTT全過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03解決java 分割字符串成數(shù)組時(shí),小圓點(diǎn)不能直接進(jìn)行分割的問(wèn)題
這篇文章主要介紹了解決java 分割字符串成數(shù)組時(shí),小圓點(diǎn)不能直接進(jìn)行分割的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12