Java 詳細講解線程安全與同步附實例與注釋
線程安全問題
多個線程可能會共享(訪問)同一個資源
比如訪問同一個對象,同一個變量,同一個文件
當多個線程訪問同一塊資源時,很容易引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全問題,稱為線程安全問題
什么情況下會出現(xiàn)線程安全問題
多個線程共享同一個資源
且至少有一個線程正在執(zhí)行寫的操作
實例:
存錢取錢問題
分別有存錢和取錢2個線程
存錢 取錢
線程1 余額 線程2
1000 《----1000------》 1000
1000+1000-----》2000
500 《-----1000-500
正確:結束后余額應該是1500,而不是500
買票問題
有賣票2個線程
賣票 賣票
線程1 票數(shù) 線程2
1000 《----1000------》 1000
1000-1-----》999
999 《-----1000-1
正確:結束后余額應該是998,而不是999
買票問題錯誤(未線程同步)實例:
public class love implements Runnable{ private int piao=3000;//有3000張票 public boolean sale() {//ture代表還有票;false代表沒有票了 if(piao<1) return false; piao--;//賣1張票 //細化piao--; //寄存器=piao; //寄存器=寄存器-1; //piao=寄存器; String sk =Thread.currentThread().getName();//獲取當前線程(買票窗口)的名字 System.out.println(sk+"賣了1張票,還剩下"+piao+"張"); return piao>1; } public void run() { while(sale());//循環(huán)執(zhí)行;直至賣完票返回false } } public class Main { public static void main(String[] a) { love tjlove =new love(); for(int i=1;i<=4;i++) {//循環(huán)4次;產(chǎn)生4個線程(窗口)賣票 Thread tj = new Thread(tjlove()); tj.setName(""+i); tj.start(); } } }
部分輸出結果:
線程安全問題
分析問題
線程A和B對類中1個變量值為17進行+1操作
最終結果為2個18
解決方案
加鎖:
過程:首先線程A先訪問到這個17,讀上來后進行加鎖并進去+1的操作改為18
并且17在加鎖期間其它線程都不能訪問
改完之后再進行寫入,然后再解鎖17
然后再由線程B去訪問它,再進行加鎖,重復上面操作變成19再解鎖
這樣做能保證在同一時間只有1個線程去訪問它,這樣就保證了安全;之前錯誤是由于這些線程一起去訪問了它
線程同步
剛剛所說的加鎖操作便是線程同步技術
可以使用線程同步技術來解決線程安全問題
線程同步在Java里有2種做法:
1.同步語句
2.同步方法
同步語句
public class love implements Runnable{ private int piao=3000;//本人cpu單核性能過強,數(shù)據(jù)量大些才能看到是4個線程在賣票 public boolean sale() { synchronized(this) {//1個線程獲取這個對象的鎖,并加鎖; synchronized作用于整個語句 //this指向當前對象 //不能用new Object();這樣會產(chǎn)生新的對象,產(chǎn)生新的鎖 //把this換成"123",效果基本一樣;因為其存在常量值里,每次訪問的對象一樣 if(piao<1) return false; piao--; String sk =Thread.currentThread().getName(); System.out.println(sk+"賣了1張票,還剩下"+piao+"張"); return piao>0; } } public void run() { while(sale()); } }
部分輸出結果:
synchronize(obj)的原理
1.每個對象都有一個與它相關的內(nèi)部鎖(intrinsic lock)或者叫監(jiān)視器鎖(monitor lock)
2.第一個執(zhí)行到同步語句的線程可以獲得 obj 的內(nèi)部鎖,在執(zhí)行完同步語句中的代碼后釋放此鎖
3.只要一個線程持有了內(nèi)部鎖,那么其它線程在同一時刻將無法再獲得此鎖
? 當它們試圖獲取此鎖時,將會進入BLOCKED狀態(tài)
4.多個線程訪問同一個 synchronized(obj)語句時
obj必須是同一個對象,才能起到同步的作用
同步方法
public class love implements Runnable{ private int piao=3000; public synchronized boolean sale() { //synchronized作用于整個方法 if(piao<1) return false; piao--; String sk =Thread.currentThread().getName(); System.out.println(sk+"賣了1張票,還剩下"+piao+"張"); return piao>0; } public void run() { while(sale()); } }
synchronized不能修飾構造方法
同步方法的本質(zhì)
實例方法:synchronized (this)
靜態(tài)方法:synchronized (Class對象)
同步語句比同步方法更靈活一點
同步語句可以精確控制需要加鎖的代碼范圍,減少處于BLOCKED狀態(tài)的線程,充分利用勞動力
使用了線程同步技術后
雖然解決了線程安全問題,但是降低了程序的執(zhí)行效率
因為加了鎖就會有處于等待的線程,多了加鎖解鎖的操作
所以在真正有必要的時候,才使用線程同步技術
到此這篇關于Java 詳細講解線程安全與同步附實例與注釋的文章就介紹到這了,更多相關Java 線程安全內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Flutter實現(xiàn)文本組件、圖標及按鈕組件的代碼
這篇文章主要介紹了Flutter實現(xiàn)文本組件、圖標及按鈕組件的代碼,本文給大家介紹的非常詳細,具有一定的參考借鑒價值 ,需要的朋友可以參考下2019-07-07MyBatis-Plus聯(lián)表查詢以及分頁代碼實例
在開發(fā)中遇到了一個問題,需要進行聯(lián)表查詢并進行分頁,因為不想自己來寫分頁,所以還是依靠MybatisPlus來實現(xiàn)想要的功能,下面這篇文章主要給大家介紹了關于MyBatis-Plus聯(lián)表查詢以及分頁的相關資料,需要的朋友可以參考下2023-06-06Java遞歸讀取文件例子_動力節(jié)點Java學院整理
本文通過一段示例代碼給大家介紹了java遞歸讀取文件的方法,代碼簡單易懂,非常不錯,具有參考借鑒價值,需要的朋友參考下吧2017-05-05