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