Java中的線程安全問(wèn)題詳細(xì)解析
線程安全
線程安全:如果有多個(gè)線程在同時(shí)運(yùn)行,而這些線程可能會(huì)同時(shí)運(yùn)行這段代碼。程序每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,此時(shí)我們就稱之為是線程安全的。
我們通過(guò)一個(gè)案例,演示線程的安全問(wèn)題:
電影院賣票,使用了A、B、C三個(gè)窗口進(jìn)行賣票,電影票總數(shù)為100張
采用線程對(duì)象來(lái)模擬賣票窗口A、B、C;使用Runnable接口的子類來(lái)模擬買的電影票
模擬電影票:
public class Ticket implements Runnable{ // 在成員位置 定義票的總數(shù)100 int ticket = 100; @Override public void run() { // 模擬買票窗口 // 買票窗口永遠(yuǎn)開啟 while (true){ // 判斷是否還有票可以賣 if(ticket > 0){ // 使用sleep增加“程序的時(shí)間”--每張票賣50ms try { Thread.sleep(50); } catch (Exception e) { e.printStackTrace(); } // 獲得線程名稱 即買票窗口名稱 String name = Thread.currentThread().getName(); System.out.println(name + "賣掉第" + ticket-- + "票"); } } } }
模擬買票:
/** * 模擬買票操作 * 假設(shè)一場(chǎng)電影有100張票 * 三個(gè)窗口同時(shí)買票 * * 窗口 線程對(duì)象 * 買票 線程任務(wù) 實(shí)現(xiàn)runnable接口 */ public class Demo { public static void main(String[] args) { // 創(chuàng)建買票任務(wù)對(duì)象 Ticket ticket = new Ticket(); // 創(chuàng)建三個(gè)窗口 Thread t1 = new Thread(ticket, "窗口A"); Thread t2 = new Thread(ticket, "窗口B"); Thread t3 = new Thread(ticket, "窗口C"); // 開啟線程 t1.start(); t2.start(); t3.start(); } }
運(yùn)行結(jié)果:
窗口A賣掉第100張票
窗口C賣掉第98張票
窗口B賣掉第99張票
窗口A賣掉第97張票
窗口B賣掉第95張票
窗口C賣掉第96張票
窗口C賣掉第94張票 ⇐
窗口B賣掉第94張票 ⇐
窗口A賣掉第94張票 ⇐
...
窗口C賣掉第1張票
窗口A賣掉第0張票
窗口B賣掉第-1張票 ⇐
發(fā)現(xiàn)程序出現(xiàn)了兩個(gè)問(wèn)題:
1. 相同的票數(shù)被賣了多次,如第94張被三個(gè)窗口都賣了
2. 賣出了不存在的票,如窗口B賣掉了第-1張票
此時(shí),幾個(gè)窗口(線程)票數(shù)不同步了,這種問(wèn)題稱為線程不安全。
線程安全問(wèn)題都是有全局變量即靜態(tài)變量引起的。若每個(gè)線程中對(duì)全局變量、靜態(tài)變量只有讀操作,而無(wú)寫操作。一般來(lái)說(shuō),這個(gè)全局變量是線程安全的;若有多個(gè)線程同時(shí)執(zhí)行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全
線程同步
當(dāng)我們使用多個(gè)線程訪問(wèn)同一資源的時(shí)候,且多個(gè)線程中對(duì)資源有些的操作,就容易出現(xiàn)線程安全問(wèn)題
要解決上述多想成并發(fā)訪問(wèn)一個(gè)資源的安全性問(wèn)題:也就是解決重復(fù)賣同一張票和賣不存在的票問(wèn)題,Java中提供了同步機(jī)制(synchronized)來(lái)解決
根據(jù)案例簡(jiǎn)述:
窗口A線程進(jìn)入操作(買票)的時(shí)候,窗口B和窗口C線程只能在外等著,窗口A操作結(jié)束,窗口A、窗口B和窗口C(CPU分配內(nèi)存是隨機(jī)的,所以還有可能是窗口A進(jìn)入)才有機(jī)會(huì)進(jìn)入代碼去執(zhí)行。
也就是說(shuō),在某個(gè)線程修改共享資源的時(shí)候,其他線程不能修改該資源,等待修改完畢同步之后,才能去搶奪CPU資源,完成對(duì)應(yīng)的操作,保證了數(shù)據(jù)的同步性,解決了線程不安全的現(xiàn)象。
為了保證每個(gè)線程都能正常執(zhí)行原子操作,Java引入了線程同步機(jī)制。
有三種方式完成同步操作:
1. 同步代碼塊
2. 同步方法
3. 鎖機(jī)制
同步代碼塊
同步代碼塊:synchronized 關(guān)鍵字可以用于方法中的某個(gè)區(qū)塊中,表示只對(duì)這個(gè)區(qū)塊的資源實(shí)行互斥訪問(wèn)。
格式:
synchronized(同步鎖){ // 需要同步的操作的代碼 }
同步鎖:
對(duì)象的同步鎖只是一個(gè)概念,可以想象為在對(duì)象上標(biāo)記了一個(gè)鎖。
1. 鎖對(duì)象可以是任意類型
2. 多個(gè)線程對(duì)象要使用同一把鎖
注意:在任何時(shí)候,最多允許一個(gè)線程擁有同步鎖,誰(shuí)拿到就進(jìn)入代碼塊,其他的線程只能在外等著
使用同步代碼塊解決賣票問(wèn)題:
/** * synchronized(鎖對(duì)象){ * * } * 1. 鎖對(duì)象可以是任意類型 * 2. 互斥線程需要使用同一把鎖 */ public class Ticket implements Runnable{ // 在成員位置 定義票的總數(shù)100 int ticket = 100; Object obj = new Object(); @Override public void run() { // 模擬買票窗口 // 買票窗口永遠(yuǎn)開啟 while (true){ // 同步鎖 synchronized (obj){ // 判斷是否還有票可以賣 if(ticket > 0){ // 使用sleep增加“程序的時(shí)間”--每張票賣50ms try { Thread.sleep(50); } catch (Exception e) { e.printStackTrace(); } // 獲得線程名稱 即買票窗口名稱 String name = Thread.currentThread().getName(); System.out.println(name + "賣掉第" + ticket-- + "票"); } } } } }
執(zhí)行結(jié)果:
窗口A賣掉第100票
窗口C賣掉第99票
窗口B賣掉第98票
窗口B賣掉第97票
...
窗口C賣掉第4票
窗口A賣掉第3票
窗口A賣掉第2票
窗口A賣掉第1票
此時(shí),每張票都只會(huì)被賣掉一次,不會(huì)存在賣掉不存在的電影票的問(wèn)題。
當(dāng)使用了同步代碼塊后,上述的線程的安全問(wèn)題即可解決
到此這篇關(guān)于Java中的線程安全問(wèn)題詳細(xì)解析的文章就介紹到這了,更多相關(guān)Java線程安全內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java生成圖片驗(yàn)證碼返回base64圖片信息方式
這篇文章主要介紹了java生成圖片驗(yàn)證碼返回base64圖片信息方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08Java實(shí)現(xiàn)爬蟲給App提供數(shù)據(jù)(Jsoup 網(wǎng)絡(luò)爬蟲)
這篇文章主要介紹了Java實(shí)現(xiàn)爬蟲給App提供數(shù)據(jù),即Jsoup 網(wǎng)絡(luò)爬蟲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-01-01springboot實(shí)現(xiàn)配置兩個(gè)parent的方法
這篇文章主要介紹了springboot實(shí)現(xiàn)配置兩個(gè)parent的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12Spring Boot thymeleaf模板引擎的使用詳解
這篇文章主要介紹了Spring Boot thymeleaf模板引擎的使用詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03java 中 poi解析Excel文件版本問(wèn)題解決辦法
這篇文章主要介紹了java 中 poi解析Excel文件版本問(wèn)題解決辦法的相關(guān)資料,需要的朋友可以參考下2017-08-08