本篇內(nèi)容介紹了“Java的Lock接口到底有什么用”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
在青山等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供網(wǎng)站設(shè)計、成都網(wǎng)站制作 網(wǎng)站設(shè)計制作專業(yè)公司,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),營銷型網(wǎng)站,成都外貿(mào)網(wǎng)站建設(shè),青山網(wǎng)站建設(shè)費(fèi)用合理。
并發(fā)編程的關(guān)鍵是什么,知道嗎?
我淡淡一笑,還好平時就玩的高并發(fā)架構(gòu)設(shè)計,不然真被你唬住了!
互斥
同一時刻,只允許一個線程訪問共享資源
同步
線程之間通信、協(xié)作
這倆問題,管程都能一把梭。JUC是通過Lock、Condition接口實(shí)現(xiàn)的管程:
Lock
解決互斥
Condition
解決同步
只見 P8 不慌不忙,又開始問道:
提起這個管程啊,synchronized也是管程的實(shí)現(xiàn)呀,既然 JDK 已經(jīng)實(shí)現(xiàn)了管程,為什么還要提供另一個實(shí)現(xiàn)?
這絕非重復(fù)造輪子,它們有很大區(qū)別。最簡單的,在JDK 1.5,synchronized性能差于Lock,但1.6后,synchronized被優(yōu)化,將性能提高,所以1.6后又推薦使用synchronized。但性能問題只要優(yōu)化一下就行了,根本無需“重復(fù)造輪子”。
問題的關(guān)鍵在于,死鎖問題的破壞“不可搶占”條件,synchronized無法達(dá)到該目的。因為synchronized申請資源時,若申請不到,線程直接就被阻塞了,而阻塞態(tài)的線程是無所作為,自然也釋放不了線程已經(jīng)占有的資源。
但我們希望:對于“不可搶占”條件,占用部分資源的線程進(jìn)一步申請其他資源時,若申請不到,可以主動釋放它已占有的資源,這樣“不可搶占”條件就被破壞掉了。
若重新設(shè)計一把互斥鎖去解決這個問題,咋搞呢?如下設(shè)計都能破壞“不可搶占”條件:
使用synchronized持有 鎖X 后,若嘗試獲取 鎖Y 失敗,則線程進(jìn)入阻塞,一旦死鎖,就再無機(jī)會喚醒阻塞線程。但若阻塞態(tài)的線程能夠響應(yīng)中斷信號,即當(dāng)給阻塞線程發(fā)送中斷信號時,能喚醒它,那它就有機(jī)會釋放曾經(jīng)持有的 鎖X。
若線程在一段時間內(nèi),都沒有獲取到鎖,不是進(jìn)入阻塞態(tài),而是返回一個錯誤,則該線程也有機(jī)會釋放曾經(jīng)持有的鎖
如果嘗試獲取鎖失敗,并不進(jìn)入阻塞狀態(tài),而是直接返回,那這個線程也有機(jī)會釋放曾經(jīng)持有的鎖
其實(shí)就是Lock接口的如下方法:
lockInterruptibly() 支持中斷
tryLock(long time, TimeUnit unit) 支持超時
tryLock() 支持非阻塞獲取鎖
Lock經(jīng)典案例就是try/finally,必須在finally塊里釋放鎖。Java多線程的可見性是通過Happens-Before規(guī)則保證的,而Happens-Before 并沒有提到 Lock 鎖。那Lock靠什么保證可見性呢?
肯定的,它是利用了volatile的Happens-Before規(guī)則。因為 ReentrantLock 的內(nèi)部類繼承了 AQS,其內(nèi)部維護(hù)了一個volatile 變量state
獲取鎖時,會讀寫state
解鎖時,也會讀寫state
所以,執(zhí)行value+=1前,程序先讀寫一次volatile state,在執(zhí)行value+=1后,又讀寫一次volatile state。根據(jù)Happens-Before的如下規(guī)則判定:
線程t1的value+=1 Happens-Before 線程t1的unlock()
由于此時 state為1,會先讀取state,所以線程t1的unlock() Happens-Before 線程t2的lock()
線程t的value+=1 Happens-Before 線程t2的lock()
可重入鎖,就是線程可以重復(fù)獲取同一把鎖,示例如下:
聽說過可重入方法嗎?orz,這是什么鬼?P8 看我一時靚仔語塞,就懂了,說到:沒關(guān)系,就隨便問問,看看你的知識面。
其實(shí)就是多線程可以同時調(diào)用該方法,每個線程都能得到正確結(jié)果;同時在一個線程內(nèi)支持線程切換,無論被切換多少次,結(jié)果都是正確的。多線程可以同時執(zhí)行,還支持線程切換。所以,可重入方法是線程安全的。
比如ReentrantLock有兩個構(gòu)造器,一個是無參構(gòu)造器,一個是傳入fair參數(shù)的。fair參數(shù)代表鎖的公平策略,true:需要構(gòu)造一個公平鎖,false:構(gòu)造一個非公平鎖(默認(rèn))。
鎖都對應(yīng)一個等待隊列,如果一個線程沒有獲得鎖,就會進(jìn)入等待隊列,當(dāng)有線程釋放鎖的時候,就需要從等待隊列中喚醒一個等待的線程。若是公平鎖,喚醒策略就是誰等待的時間長,就喚醒誰,這很公平 若是非公平鎖,則不提供這個公平保證,所以可能等待時間短的線程被先喚醒。非公平鎖的場景應(yīng)該是線程釋放鎖之后,如果來了一個線程獲取鎖,他不必去排隊直接獲取到,不會入隊。獲取不到才入隊。
鎖并非解決并發(fā)問題的銀彈,風(fēng)險很高,比如各種隨處可見的死鎖,還影響性能。并發(fā)大師Doug Lea的最佳實(shí)踐:
永遠(yuǎn)只在更新對象的成員變量時加鎖
永遠(yuǎn)只在訪問可變的成員變量時加鎖
永遠(yuǎn)不在調(diào)用其他對象的方法時加鎖 因為調(diào)用其他對象的方法,實(shí)在是太不安全了,也許“其他”方法里面有線程sleep()的調(diào)用,也可能會有奇慢無比的I/O操作,這些都會嚴(yán)重影響性能。更可怕的是,“其他”類的方法可能也會加鎖,然后雙重加鎖就可能導(dǎo)致死鎖。
還有一些常見的比如只在該加鎖的地方加鎖。
notifyAll() 在面對公平鎖和非公平鎖的時候,效果一樣。所有等待隊列中的線程全部被喚醒,統(tǒng)統(tǒng)到入口等待隊列中排隊?這些被喚醒的線程不用根據(jù)等待時間排隊再放入入口等待隊列中了吧?都被喚醒。理論上是同時進(jìn)入入口等待隊列,等待時間是相同的。
CPU層面的原子性是單條cpu指令。Java層面的互斥(管程)保證了原子性。這兩個原子性意義不一樣。cpu的原子性是不受線程調(diào)度影響,指令要不執(zhí)行了,要么沒執(zhí)行。而Java層面的原子性是在鎖的機(jī)制下保證只有一個線程執(zhí)行,其余等待,此時cpu還是可以進(jìn)行線程調(diào)度,使運(yùn)行中的那個線程讓出cpu時間,當(dāng)然了該線程還是掌握鎖。
“Java的Lock接口到底有什么用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
本文題目:Java的Lock接口到底有什么用
文章起源:http://muchs.cn/article36/jpdgsg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計、小程序開發(fā)、網(wǎng)站內(nèi)鏈、建站公司、網(wǎng)站改版、網(wǎng)頁設(shè)計公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)