第一章 走進并行世界
1缤骨、臨界區(qū)
表示共享資源或者共享數(shù)據(jù)
2浮禾、同步與異步
如果系統(tǒng)中存在臨界資源(資源數(shù)量少于競爭資源的線程數(shù)量的資源)伏嗜,例如正在寫的數(shù)據(jù)以后可能被另一個線程讀到,或者正在讀的數(shù)據(jù)可能已經(jīng)被另一個線程寫過了伐厌,那么這些數(shù)據(jù)就必須進行同步存瘸谐瘛(數(shù)據(jù)庫操作中的排他鎖就是最好的例子)。當(dāng)應(yīng)用程序在對象上調(diào)用了一個需要花費很長時間來執(zhí)行的方法挣轨,并且不希望讓程序等待方法的返回時军熏,就應(yīng)該使用異步編程,在很多情況下采用異步途徑往往更有效率卷扮。事實上荡澎,所謂的同步就是指阻塞式操作,而異步就是非阻塞式操作晤锹。
3摩幔、死鎖、饑餓鞭铆、活鎖
死鎖:多個線程互現(xiàn)持有對方手中的資源或衡,多個線程一起進去阻塞狀態(tài)
饑餓:高優(yōu)先級線程插隊,低優(yōu)先級線程無法獲得資源车遂,但未來有可能得到解決
活鎖:讓路問題封断,線程謙讓,主動釋放所持有地資源舶担,導(dǎo)致資源不斷地在兩個線程之間跳動而又無法進行下去
【面試題】死鎖的解決方案:
· 死鎖出現(xiàn)的原因是因為各個線程均不肯釋放手中的資源而直接進入阻塞狀態(tài)坡疼,因此可以使用可重入鎖ReentrantLock的中斷響應(yīng)和限時等待來解決
①中斷響應(yīng):lock.lockInterruptibly(); 等待鎖時可以被中斷,中斷響應(yīng)邏輯中可以釋放已持有的鎖資源
②鎖申請等待限時:lock.tryLock(時間)衣陶,規(guī)定時間內(nèi)等不到返回false柄瑰,可以采取釋放鎖的操作
· 釋放所持有的鎖并不意味著操作失敗,可以在外層設(shè)置循環(huán)剪况,再重新嘗試獲取鎖資源教沾,直到成功。
4拯欧、并發(fā)級別
阻塞详囤、無饑餓、無障礙镐作、無鎖藏姐、無等待
· 無饑餓:公平鎖
·無障礙:自由進出,遇到?jīng)_突回滾操作(不循環(huán))该贾。例如一致性標(biāo)志法檢測沖突
·無鎖:自由進出羔杨,遇到?jīng)_突循環(huán)重試,最終總有能走出臨界區(qū)的杨蛋,例如CAS鎖
·無等待:訪問只有兜材,修改加鎖并檢測沖突
5、加速比
· 加速比 = time優(yōu)化前 / time優(yōu)化后
· 不僅取決于CPU的核數(shù)逞力,還取決于串行比例
6曙寡、JMM:原子性、可見性寇荧、有序性
· 原子性:
基本举庶、引用數(shù)據(jù)類型的賦值和引用是原子性操作。
但在32位的機子中揩抡,long和double的引用和賦值則是可分割的户侥。
· 有序性:(happen-before原則)
指令重排問題,為了減少中斷流水線的次數(shù)峦嗤,在匯編指令層面上蕊唐。
指令重排的僅保證串行語義的一致心褐,但沒有義務(wù)保證多線程之間的語義也一直缸沃,因此多線程時不能保證有序性豺裆。
哪些指令不能重排:happen-before規(guī)則
第二章 Java并行程序基礎(chǔ)
2.1 進程
· 進程是系統(tǒng)資源分配和調(diào)度的基本單位
· 進程是程序的實體
· 線程是輕量級進程逸尖,是程序執(zhí)行的最小單位
· 使用多線程而不是多進程的原因知染?
線程間切換和調(diào)度的成本遠(yuǎn)小于進程
· 線程狀態(tài)6種:new胃碾、runable陆爽、waiting胶逢、timed waiting曹体、blocked俗扇、terminated
2.2 線程基本操作
· 不要用run方法來開啟線程,他只會在當(dāng)前線程中串行地執(zhí)行run方法中的代碼
· 創(chuàng)建線程三種方式
2.2.2 終止線程
· Thread.stop():太過暴力箕别,會直接強制終止線程铜幽,并釋放其持有的所有資源,包括鎖串稀,引發(fā)一致性問題
可以通過增加stopme標(biāo)志和對應(yīng)標(biāo)志函數(shù)來實現(xiàn)安全的終止
2.2.3 線程中斷(interrupt)
· 需要自己在run方法中增加中斷處理邏輯
2.2.4 等待和通知(wait和notify)
· Object類中的方法
· 二者在使用前均需要獲得目標(biāo)對象的一個監(jiān)視器除抛,因此他必須被包含在synchronized語句中
· notify執(zhí)行后僅喚醒在目標(biāo)對象的等待隊列中的線程,使其進入runable狀態(tài)母截,但自己并不會立馬釋放該對象的監(jiān)視器
· wait()和sleep()的區(qū)別:sleep()不會釋放任何資源
2.2.5 掛起和繼續(xù)執(zhí)行(suspend和resume)
· suspend不會釋放任何鎖資源到忽,直到調(diào)用resume
· 因此不推薦,已被棄用。若resume在suspend前執(zhí)行(在不同線程中調(diào)用時會出現(xiàn)這種情況)喘漏,則線程會一直處于掛起狀態(tài)
· 可以利用wait()和notify()方法护蝶,在應(yīng)用層面實現(xiàn)suspend()和resume()的功能(會釋放鎖資源)
2.2.6 等待線程結(jié)束(join)和謙讓(yield)
· 線程1.join() :需先調(diào)用start方法,再用join(), 暫停當(dāng)前線程翩迈,無限期等調(diào)用線程結(jié)束持灰,也有含參方法,可設(shè)置等待時間负饲。
其本質(zhì)是讓調(diào)用線程wait在線程對象實例上堤魁。
因此在應(yīng)用程序中,盡量不要使用線程對象作為鎖對象返十,避免出現(xiàn)錯誤
· yield() :讓出當(dāng)前cpu妥泉,進入runable狀態(tài),等待cpu重新調(diào)度洞坑,當(dāng)然也可能還是他自己搶到cpu執(zhí)行權(quán)
2.3 volatile與Java內(nèi)存模型(JMM)
· 在虛擬機的Server模型下盲链,由于系統(tǒng)優(yōu)化的結(jié)果,臨界區(qū)數(shù)據(jù)的修改并不一定發(fā)生能被其他線程發(fā)現(xiàn)
· volatile關(guān)鍵字:高速虛擬機检诗,該變量是不穩(wěn)定的匈仗,會在不同的線程中被修改,每次使用記得實時地去臨界區(qū)看看最新的數(shù)據(jù)
· volatile保證修改可見性
2.4 分門別類管理:線程組
ThreadGroup:方便統(tǒng)一管理和進行一些統(tǒng)計
2.5 駐守后臺:守護線程(Daemon)
· 是系統(tǒng)的守護者逢慌,負(fù)責(zé)為用戶(工作)線程提供服務(wù)悠轩,例如垃圾回收線程、JIT線程就可以理解為守護線程攻泼。但一個java應(yīng)用內(nèi)只有守護線程時火架,虛擬機就會自然退出。
2.6 線程優(yōu)先級
· 1-10忙菠,數(shù)值越大優(yōu)先級越高
2.7 線程安全的概念與synchronized
· 作用是實現(xiàn)線程間的同步
· 同步代碼塊何鸡、同步方法、靜態(tài)同步方法的鎖對象
· synchronized可以保證多線程的有序性和可靠性
· synchronized既保證了可見性又保證了訪問共享變量的原子性
· synchronized原理:
獲得鎖
清空線程變量副本
拷貝共享變量到線程變量副本
執(zhí)行代碼
將修改后的變量副本拷貝到共享數(shù)據(jù)區(qū)
釋放鎖
2.8 隱蔽的錯誤
· 并發(fā)下的ArrayList
數(shù)組越界問題:保存容器大小的變量被多線程不正常的訪問
數(shù)據(jù)丟失問題:多個線程對容器同一個位置進行賦值
· 并發(fā)下的HashMap
jdk 1.7:多個線程操作容器牛欢,執(zhí)行擴容操作的數(shù)據(jù)遷移函數(shù)transfer函數(shù)時骡男,有可能造成數(shù)據(jù)丟失和鏈表循環(huán)的情況
jdk 1.8:解決了之前的問題。將頭插改為尾插傍睹,并將數(shù)據(jù)遷移操作合并到了resize函數(shù)中隔盛,然而在多個線程進行put操作時存在數(shù)據(jù)覆蓋的問題
· 加鎖的注意事項
不要把不可變類型對象當(dāng)作鎖對象,例如String拾稳、Integer吮炕,因為當(dāng)進行拼接或計算時,這類引用會重新指向新的對象访得,并發(fā)操作時就可能獲取到不同的對象
面試題:
合適的線程數(shù)量是多少龙亲?CPU核心數(shù)和線程數(shù)的關(guān)系?
https://blog.csdn.net/qq_29860591/article/details/113618636
①CPU密集型任務(wù):
首先,我們來看 CPU 密集型任務(wù)鳄炉,比如加密杜耙、解密、壓縮迎膜、計算等一系列需要大量耗費 CPU 資源的任務(wù)泥技。對于這樣的任務(wù)最佳的線程數(shù)為 CPU 核心數(shù)的 1~2 倍浆兰,如果設(shè)置過多的線程數(shù)磕仅,實際上并不會起到很好的效果。此時假設(shè)我們設(shè)置的線程數(shù)量是 CPU 核心數(shù)的 2 倍以上簸呈,因為計算任務(wù)非常重榕订,會占用大量的 CPU 資源,所以這時 CPU 的每個核心工作基本都是滿負(fù)荷的蜕便,而我們又設(shè)置了過多的線程劫恒,每個線程都想去利用 CPU 資源來執(zhí)行自己的任務(wù),這就會造成不必要的上下文切換轿腺,此時線程數(shù)的增多并沒有讓性能提升两嘴,反而由于線程數(shù)量過多會導(dǎo)致性能下降。針對這種情況族壳,我們最好還要同時考慮在同一臺機器上還有哪些其他會占用過多 CPU 資源的程序在運行憔辫,然后對資源使用做整體的平衡。
②耗時IO型任務(wù):
第二種任務(wù)是耗時 IO 型仿荆,比如數(shù)據(jù)庫贰您、文件的讀寫,網(wǎng)絡(luò)通信等任務(wù)拢操,這種任務(wù)的特點是并不會特別消耗 CPU 資源锦亦,但是 IO 操作很耗時,總體會占用比較多的時間令境。對于這種任務(wù)最大線程數(shù)一般會大于 CPU 核心數(shù)很多倍杠园,因為IO 讀寫速度相比于 CPU 的速度而言是比較慢的,如果我們設(shè)置過少的線程數(shù)舔庶,就可能導(dǎo)致 CPU 資源的浪費抛蚁。而如果我們設(shè)置更多的線程數(shù),那么當(dāng)一部分線程正在等待 IO 的時候栖茉,它們此時并不需要 CPU 來計算篮绿,那么另外的線程便可以利用 CPU 去執(zhí)行其他的任務(wù),互不影響吕漂,這樣的話在任務(wù)隊列中等待的任務(wù)就會減少亲配,可以更好地利用資源。
作者:瘋狂麥克斯鴨
鏈接:http://www.reibang.com/p/26d4d667f9fd
來源:簡書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)吼虎,非商業(yè)轉(zhuǎn)載請注明出處犬钢。