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