多線程編程中鎖如何保證自己是線程安全的?
線程安全問題概述賣票問題分析單窗口賣票
一個(gè)窗口(單線程)賣100張票沒有問題單線程程序是不會(huì)出現(xiàn)線程安全問題的多個(gè)窗口賣不同的票3個(gè)窗口一起賣票,賣的票不同,也不會(huì)出現(xiàn)問題多線程程序,沒有訪問共享數(shù)據(jù),不會(huì)產(chǎn)生問題多個(gè)窗口賣相同的票3個(gè)窗口賣的票是一樣的,就會(huì)出現(xiàn)安全問題多線程訪問了共享的數(shù)據(jù),會(huì)產(chǎn)生線程安全問題線程安全問題代碼實(shí)現(xiàn)模擬賣票案例創(chuàng)建3個(gè)線程,同時(shí)開啟,對(duì)共享的票進(jìn)行出售線程安全問題原理分析線程安全問題產(chǎn)生原理圖分析:線程安全問題正常是不允許產(chǎn)生的,我們可以讓一個(gè)線程在訪問共享數(shù)據(jù)的時(shí)候,無論是否失去了cpu的執(zhí)行權(quán);讓其他的線程只能等待,等待當(dāng)前線程賣完票,其他線程在進(jìn)行賣票。解決線程安全問題辦法1-synchronized同步代碼塊同步代碼塊:synchronized 關(guān)鍵字可以用于方法中的某個(gè)區(qū)塊中,表示只對(duì)這個(gè)區(qū)塊的資源實(shí)行互斥訪問。使用synchronized同步代碼塊格式:synchronized(鎖對(duì)象){可能會(huì)出現(xiàn)線程安全問題的代碼(訪問了共享數(shù)據(jù)的代碼)}代碼實(shí)現(xiàn)如下:注意:代碼塊中的鎖對(duì)象,可以使用任意的對(duì)象。但是必須保證多個(gè)線程使用的鎖對(duì)象是同一個(gè)。鎖對(duì)象作用:把同步代碼塊鎖住,只讓一個(gè)線程在同步代碼塊中執(zhí)行。同步技術(shù)原理分析同步技術(shù)原理:使用了一個(gè)鎖對(duì)象,這個(gè)鎖對(duì)象叫同步鎖,也叫對(duì)象鎖,也叫對(duì)象監(jiān)視器3個(gè)線程一起搶奪cpu的執(zhí)行權(quán),誰搶到了誰執(zhí)行run方法進(jìn)行賣票。t0搶到了cpu的執(zhí)行權(quán),執(zhí)行run方法,遇到synchronized代碼塊這時(shí)t0會(huì)檢查synchronized代碼塊是否有鎖對(duì)象發(fā)現(xiàn)有,就會(huì)獲取到鎖對(duì)象,進(jìn)入到同步中執(zhí)行t1搶到了cpu的執(zhí)行權(quán),執(zhí)行run方法,遇到synchronized代碼塊這時(shí)t1會(huì)檢查synchronized代碼塊是否有鎖對(duì)象發(fā)現(xiàn)沒有,t1就會(huì)進(jìn)入到阻塞狀態(tài),會(huì)一直等待t0線程歸還鎖對(duì)象,t0線程執(zhí)行完同步中的代碼,會(huì)把鎖對(duì)象歸 還給同步代碼塊t1才能獲取到鎖對(duì)象進(jìn)入到同步中執(zhí)行總結(jié):同步中的線程,沒有執(zhí)行完畢不會(huì)釋放鎖,同步外的線程沒有鎖進(jìn)不去同步。解決線程安全問題辦法2-synchronized普通同步方法同步方法:使用synchronized修飾的方法,就叫做同步方法,保證A線程執(zhí)行該方法的時(shí)候,其他線程只能在方法外等著。格式:public synchronized void payTicket(){可能會(huì)出現(xiàn)線程安全問題的代碼(訪問了共享數(shù)據(jù)的代碼)}代碼實(shí)現(xiàn):分析:定義一個(gè)同步方法,同步方法也會(huì)把方法內(nèi)部的代碼鎖住,只讓一個(gè)線程執(zhí)行。同步方法的鎖對(duì)象是誰?就是實(shí)現(xiàn)類對(duì)象 new RunnableImpl(),也是就是this,所以同步方法是鎖定的this對(duì)象。解決線程安全問題辦法3-synchronized靜態(tài)同步方法同步方法:使用synchronized修飾的方法,就叫做同步方法,保證A線程執(zhí)行該方法的時(shí)候,其他線程只能在方法外等著。對(duì)于static方法,我們使用當(dāng)前方法所在類的字節(jié)碼對(duì)象(類名.class)。格式:public static synchronized void payTicket(){可能會(huì)出現(xiàn)線程安全問題的代碼(訪問了共享數(shù)據(jù)的代碼)}代碼實(shí)現(xiàn):分析:靜態(tài)的同步方法鎖對(duì)象是誰?不能是this,this是創(chuàng)建對(duì)象之后產(chǎn)生的,靜態(tài)方法優(yōu)先于對(duì)象靜態(tài)方法的鎖對(duì)象是本類的class屬性–>class文件對(duì)象(反射)。解決線程安全問題辦法4-Lock鎖Lock接口中的方法:public void lock() :加同步鎖。public void unlock() :釋放同步鎖使用步驟:在成員位置創(chuàng)建一個(gè)ReentrantLock對(duì)象在可能會(huì)出現(xiàn)安全問題的代碼前調(diào)用Lock接口中的方法lock獲取鎖在可能會(huì)出現(xiàn)安全問題的代碼后調(diào)用Lock接口中的方法unlock釋放鎖代碼實(shí)現(xiàn):分析:java.util.concurrent.locks.Lock接口Lock 實(shí)現(xiàn)提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。相比Synchronized,ReentrantLock類提供了一些高級(jí)功能,主要有以下3項(xiàng):等待可中斷,持有鎖的線程長期不釋放的時(shí)候,正在等待的線程可以選擇放棄等待,這相當(dāng)于Synchronized來說可以避免出現(xiàn)死鎖的情況。通過lock.lockInterruptibly()來實(shí)現(xiàn)這個(gè)機(jī)制。公平鎖,多個(gè)線程等待同一個(gè)鎖時(shí),必須按照申請(qǐng)鎖的時(shí)間順序獲得鎖,Synchronized鎖非公平鎖,ReentrantLock默認(rèn)的構(gòu)造函數(shù)是創(chuàng)建的非公平鎖,可以通過參數(shù)true設(shè)為公平鎖,但公平鎖表現(xiàn)的性能不是很好。公平鎖、非公平鎖的創(chuàng)建方式:鎖綁定多個(gè)條件,一個(gè)ReentrantLock對(duì)象可以同時(shí)綁定多個(gè)對(duì)象。ReenTrantLock提供了一個(gè)Condition(條件)類,用來實(shí)現(xiàn)分組喚醒需要喚醒的線程們,而不是像synchronized要么隨機(jī)喚醒一個(gè)線程要么喚醒全部線程。ReentrantLock和Synchronized的區(qū)別相同點(diǎn):它們都是加鎖方式同步;都是重入鎖;阻塞式的同步;也就是說當(dāng)如果一個(gè)線程獲得了對(duì)象鎖,進(jìn)入了同步塊,其他訪問該同步塊的線程都必須阻塞在同步塊外面等待,而進(jìn)行線程阻塞和喚醒的代價(jià)是比較高的(操作系統(tǒng)需要在用戶態(tài)與內(nèi)核態(tài)之間來回切換,代價(jià)很高,不過可以通過對(duì)鎖優(yōu)化進(jìn)行改善);