java面试线程必备知识点,怼死面试官,从我做起
轉(zhuǎn)載自?java面試線程必備知識(shí)點(diǎn),懟死面試官,從我做起
|--多線程一定好么?
cpu密集不好 io密集好
|--如何減少上下文切換:
無(wú)鎖并發(fā)(數(shù)據(jù)id根據(jù)Hash分段)、CAS、最少線程
|--java線程避免死鎖:
避免一個(gè)線程同時(shí)有多個(gè)鎖
避免一個(gè)鎖占用多個(gè)資源
lock.tryLock代替內(nèi)部鎖
內(nèi)存屏障:限制命令操作順序,有LoadLoad、LoadStore、LoadStore、StroreStreo四種屏障
緩沖行:cpu緩存最小儲(chǔ)存單位
寫命中:緩存有,直接寫入緩存
緩存一致性:主存改變,其他緩存改變(read、load、use綁定)
順序一致性:單個(gè)線程內(nèi)執(zhí)行結(jié)果一定是不變的(但依然有指令重排,只是結(jié)果不受影響的重排)
|--八個(gè)CPU原子命令:
lock、unlock、read、load、use、assign、store、write
|--volatile做的事:
1.lock前綴指令使緩存行立即寫入內(nèi)存(assign、store、write綁定)
2.其他cpu緩存無(wú)效
3.加入內(nèi)存屏障
使用前景:不依賴于上次數(shù)據(jù)
使用案例:i++:tmp = i;tmp=tmp+1;i = tmp;
64位機(jī)器跑32位jvm,long和double:2段分2次計(jì)算,不加volatile會(huì)導(dǎo)致結(jié)果前32位是一個(gè)線程結(jié)果,后32位一個(gè)線程結(jié)果
|--synchronized
對(duì)象加鎖,Monitor對(duì)象,monitorenter和monitorexit命令實(shí)現(xiàn)
鎖升級(jí)
|--ReentrantLock 可重入鎖
通過(guò)CVS等實(shí)現(xiàn),比synchronized效率略高,有公平鎖非公平鎖
鎖可多次進(jìn)入,并把擁有數(shù)++
lock(), 如果獲取了鎖立即返回,如果別的線程持有鎖,當(dāng)前線程則一直處于休眠狀態(tài),直到獲取鎖
tryLock(), 如果獲取了鎖立即返回true,如果別的線程正持有鎖,立即返回false;
|--ReentrantReadWriteLock
read/write兩把鎖
寫鎖與ReetrantLock類似,只有寫鎖讀鎖都沒(méi)被占用才獲得鎖
讀鎖擁有數(shù)是多個(gè)線程的,每個(gè)線程擁有數(shù)只能自己通過(guò)ThreadLocal記錄
寫鎖結(jié)束降級(jí)讀鎖,避免可見(jiàn)性問(wèn)題
|--Lock和synchronized區(qū)別
Lock是通過(guò)代碼級(jí)實(shí)現(xiàn),cvs
synchronized是通過(guò)jvm的monitor實(shí)現(xiàn)的
還多了 鎖投票,定時(shí)鎖等候和中斷鎖等候等特性
使用ReentrantLock,如果A不釋放,可以使B在等待了足夠長(zhǎng)的時(shí)間以后,中斷等待,而干別的事情
|--AQS(AbstractQueuedSynchronized)
有隊(duì)列,有state、進(jìn)入會(huì)先自旋再阻塞,默認(rèn)非公平,隊(duì)列喚醒了調(diào)用tryAcquire,不一定能獲取鎖
|--java對(duì)象頭:
MarkWord 長(zhǎng)度:32/64,存儲(chǔ)hashCode或者鎖信息
|--CAS unsafe.compareAndSwap(對(duì)象地址,原來(lái)值,要修改值)
unsafe是通過(guò)操作系統(tǒng)實(shí)現(xiàn)(CMPXCHG指令),如果失敗返回false;
|--CAS使用:自旋鎖、自適應(yīng)自旋鎖
|--鎖的升級(jí)
偏向鎖(markword指向所在線程,代價(jià)低,兩個(gè)線程則停安全點(diǎn)撤銷)->輕量級(jí)線程(markword置換到擁有者線程,線程對(duì)象互指。兩個(gè)線程則b線程自旋等待)->重量級(jí)鎖(syn、reeentrantLock)
比較:
偏向鎖: ?加鎖解鎖消耗極少,鎖競(jìng)爭(zhēng)的安全點(diǎn)帶來(lái)消耗。 ? ? ?適用于一個(gè)線程
輕量級(jí)鎖:響應(yīng)快,自旋消耗cpu ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?追求響應(yīng)時(shí)間,同步塊非常快
重量級(jí): ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?追求吞吐量,同步塊執(zhí)行時(shí)間長(zhǎng)
|--處理器實(shí)現(xiàn)原子性策略:
LOCK#信號(hào)是一個(gè)線程獨(dú)占共享內(nèi)存(通過(guò)鎖住主內(nèi)存總線,之后優(yōu)化成緩存鎖)
緩存鎖保證原子性
|--java原子實(shí)現(xiàn):
鎖和CAS
CAS局限性:ABA問(wèn)題(過(guò)程無(wú)感知)、循環(huán)時(shí)間開(kāi)銷大、一次保證一個(gè)變量
|--內(nèi)存模型(對(duì)底層抽象)
線程通信方式:
內(nèi)存模型(共享內(nèi)存)
消息傳遞(A復(fù)制到主內(nèi)存,再?gòu)闹鲀?nèi)存寫到B)
管道:輸入流輸出流(PipedReader,PipedWriter,PipedInputstream,PipedOutputStream)
內(nèi)存模型:本地內(nèi)存(共享變量副本、局部變量)、主內(nèi)存(共享變量)
指令重排序:編譯優(yōu)化重排、并行重排、內(nèi)存重排
|--final域重寫規(guī)則
構(gòu)造函數(shù)內(nèi),final寫入與被構(gòu)造的對(duì)象引用賦值不能重排序(obj=this會(huì)引發(fā)逃逸,例如此時(shí)別的線程調(diào)用obj.i,final的i變量還沒(méi)初始化)
初次讀含final域?qū)ο笈c隨后讀final區(qū)域不能重排
|--單例模式問(wèn)題
實(shí)例化分為:1.開(kāi)辟空間memory 2.初始化對(duì)象 3.設(shè)置instance指向memory。
指令重排可能是:1->3->2 , 若2還未執(zhí)行,B線程認(rèn)為instance非空,直接調(diào)用instance,導(dǎo)致錯(cuò)誤
解決方案:1.volatile禁止重排序 2.匿名內(nèi)部類(連自己加鎖都不用,類自帶實(shí)例化鎖)
|--為什么使用多線程
1.多處理器發(fā)揮功效
2.更快相應(yīng),一個(gè)下訂單帶來(lái)一系列操作如何快速成功:線程派發(fā),分任務(wù)執(zhí)行
|--java優(yōu)先級(jí)
不一定有用,主要是靠操作系統(tǒng)底層實(shí)現(xiàn)
|--interrupt
interrupt不會(huì)真的終止,只是一種協(xié)作機(jī)制
interrupt()將會(huì)設(shè)置該線程的中斷狀態(tài)位,即設(shè)置為true
使用Thread.currentThread().isInterrupted()方法(因?yàn)樗鼘⒕€程中斷標(biāo)示位設(shè)置為true后,不會(huì)立刻清除中斷標(biāo)示位,即不會(huì)將中斷標(biāo)設(shè)置為false)
thread.interrupted()(該方法調(diào)用后會(huì)將中斷標(biāo)示位清除,即重新設(shè)置為false)
一個(gè)線程處于了等待狀態(tài)(thread.sleep、thread.join、thread.wait),則在線程在檢查中斷標(biāo)示時(shí)如果發(fā)現(xiàn)中斷標(biāo)示為true,則會(huì)在這些阻塞方法調(diào)用處拋出InterruptedException異常,并且在拋出異常后立即將線程的中斷標(biāo)示位清除,即重新設(shè)置為false。拋出異常是為了線程從阻塞狀態(tài)醒過(guò)來(lái),并在結(jié)束線程前讓程序員有足夠的時(shí)間來(lái)處理中斷請(qǐng)求。
鎖的情況下不會(huì)被中斷影響
|--阻塞狀態(tài)與等待區(qū)別
阻塞是進(jìn)鎖里,等待是wait、sleep。sleep設(shè)置時(shí)間狀態(tài)叫做超時(shí)等待狀態(tài)
|--線程的應(yīng)用
1.等待之后超時(shí)
while(結(jié)果未返回 && 時(shí)間未到)
wait();
2.線程池
要有隊(duì)列,狀態(tài)
Worker實(shí)現(xiàn)Runnable接口,循環(huán)從jobs隊(duì)列取任務(wù)執(zhí)行,獲取不到就wait();
execute(Job job)時(shí),喚醒jobs
3.基于線程池Web服務(wù)器
思路:開(kāi)一個(gè)Socket服務(wù),每次accept后,把這個(gè)一對(duì)一服務(wù)放封裝成job類,放到j(luò)obs隊(duì)列里
|--LockSupport
工具類,有park、unpark阻塞喚醒線程
|--Condition
相當(dāng)于Lock中的wait和notify,區(qū)別是wait等待隊(duì)列只能有一個(gè),Condition可以有多個(gè)
Condition隊(duì)列類似于AQS隊(duì)列
每個(gè)Condition下面有一個(gè)等待await的等待隊(duì)列
Lock.newCondition()獲取condition
Lock.await(); = wait
Lock.singal();=notify
|--ConcurrentHashMap
問(wèn)題:HashMap線程不安全導(dǎo)致Entry鏈表編程環(huán),引發(fā)死循環(huán)。
HashTable效率低
解決:Segment包含HashEntry數(shù)組
Segment是一種可重入鎖(ReentrantLock)
實(shí)現(xiàn):segment數(shù)量是2的n次方,默認(rèn)16
每一個(gè)segment的容量=每個(gè)segment里HashEntry*負(fù)載因子
如何放入數(shù)據(jù):再散列確保數(shù)據(jù)分散后放入segment
get方法:不加鎖,而是用volatile
1.8更新:沒(méi)有了segment,橫向用Node鏈表替代,Node被調(diào)用取時(shí)就synchronize加鎖。當(dāng)沒(méi)Node底下鏈表超過(guò)8個(gè),將加鎖
|--ConcurrentLinkedQueue
非阻塞
入隊(duì):定位尾節(jié)點(diǎn),不成功cvs重試(為了減少CVS,控制尾節(jié)點(diǎn)更新頻率)
出隊(duì):
總結(jié)
以上是生活随笔為你收集整理的java面试线程必备知识点,怼死面试官,从我做起的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 阿凡达的大结局是怎样的 阿凡达的大结局是
- 下一篇: 十分钟快速了解 ES6 Promise