Java 代碼最終是被翻譯成機器碼執(zhí)行的魂迄,機器碼才是真正可以和硬件電路交互的代碼荡灾。
什么是阻塞式方法舆驶?
阻塞式方法是指程序會一直等待渊胸,在該方法完成期間不做任何其他的事情旬盯,ServerSocket
的 accept()
方法就是一直在等待客戶端的連接。這里的阻塞是指調(diào)用結(jié)果返回之前翎猛,當(dāng)前線程會被掛起胖翰,直到得到結(jié)果之后才會返回。此外切厘,還有異步和非阻塞式方法在任務(wù)完成前就返回萨咳。
Java 中堆和棧有什么不同?
每個線程都有自己的棧內(nèi)存疫稿,用于存儲本地變量培他,方法參數(shù)和棧調(diào)用,一個線程中存儲的變量對其它線程是不可見的遗座。而堆是所有線程共享的一片公用內(nèi)存區(qū)域舀凛。對象都在堆里創(chuàng)建,為了提升效率線程會從堆中弄一個緩存到自己的棧途蒋,如果多個線程使用該變量就可能引發(fā)問題猛遍,這時 volatile 變量就可以發(fā)揮作用了,它要求線程從主存中讀取變量的值号坡。
為什么你應(yīng)該在循環(huán)中檢查等待條件懊烤?
處于 等待狀態(tài)的線程
可能會收到錯誤警報和偽喚醒,如果不在循環(huán)中檢查等待條件宽堆,程序就會在沒有滿足結(jié)束條件的情況下退出腌紧。因此,當(dāng)一個等待線程醒來時日麸,不能認為它原來的等待狀態(tài)仍然是有效的寄啼,在 notify()方法調(diào)用之后和等待線程醒來之前這段時間它可能會改變。這就是在循環(huán)中使用 wait()方法效果更好的原因代箭。
為什么 wait 和 notify 方法要在同步塊中調(diào)用?
主要是因為 Java API 強制要求這樣做涕刚,如果你不這么做嗡综,你的代碼會拋出IllegalMonitorStateException
異常。還有一個原因是為了避免 wait 和 notify 之間產(chǎn)生競態(tài)條件杜漠。
Java 中 notify 和 notifyAll 有什么區(qū)別极景?
notify() 方法不能喚醒某個具體的線程察净,所以只有一個線程在等待的時候它才有用武之地。而 notifyAll() 會喚醒所有的線程并允許他們爭奪鎖盼樟,確保至少有一個等待的線程能繼續(xù)運行氢卡。
Java 中用到了什么線程調(diào)度算法?
搶占式晨缴。一個線程用完 CPU 之后译秦,操作系統(tǒng)會根據(jù) 線程優(yōu)先級
、線程饑餓情況
等數(shù)據(jù)算出一個總的優(yōu)先級并分配下一個時間片給某個線程執(zhí)行击碗。
Thread.sleep(0) 的作用是什么筑悴?
由于 Java 采用搶占式的線程調(diào)度算法,因此可能會出現(xiàn)某條線程經(jīng)常能獲取到 CPU 的控制權(quán)稍途,為了讓某些優(yōu)先級比較低的線程也能獲取到 CPU 控制權(quán)阁吝,可以使用 Thread.sleep(0)
手動觸發(fā)一次操作系統(tǒng)分配時間片的操作,這也是平衡 CPU 控制權(quán)的一種操作械拍。
多線程上下文切換是什么意思突勇?
多線程的上下文切換是指 CPU 控制權(quán)
由一個已經(jīng)正在運行的線程切換到另外一個就緒并等待獲取 CPU 執(zhí)行權(quán)的線程的過程。說白了就是 CPU 控制權(quán)交接的過程坷虑。
怎么喚醒一個阻塞的線程与境?
如果線程是因為調(diào)用了 wait()
、sleep()
或者 join()
方法而導(dǎo)致的阻塞猖吴,可以通過拋出 InterruptedException
來喚醒它摔刁;如果線程遇到了 IO 阻塞,無能為力海蔽,因為 IO 是操作系統(tǒng)實現(xiàn)的共屈,Java 代碼并沒有辦法直接接觸到操作系統(tǒng)。
Thread.currentThread().interrupt();
ThreadLocal 是什么党窜?有什么應(yīng)用場景拗引?
ThreadLocal 的作用是提供線程內(nèi)的局部變量,這種變量在線程的生命周期內(nèi)起作用幌衣,減少同一個線程內(nèi)多個函數(shù)或者組件之間一些公共變量的傳遞的復(fù)雜度矾削。用來解決數(shù)據(jù)庫連接、Session 管理等豁护。
Jdk 中排查多線程問題用什么命令哼凯?
jstack
什么是 CAS 算法?在多線程中有哪些應(yīng)用楚里。
CAS断部,全稱為 Compare and Swap,即比較-替換班缎。假設(shè)有三個操作數(shù):內(nèi)存值 V蝴光、預(yù)期值 A她渴、要修改的值 B,當(dāng)且僅當(dāng)預(yù)期值 A 和內(nèi)存值 V 相同時蔑祟,才會將內(nèi)存值修改為 B 并返回 true趁耗,否則什么都不做并返回 false。當(dāng)然 CAS 一定要 volatile 變量配合疆虚,這樣才能保證每次拿到的變量是主內(nèi)存中最新的那個值苛败,java.util.concurrent.atomic
包下面的 Atom****類都有 CAS 算法的應(yīng)用。
線程數(shù)過多會造成什么異常装蓬?
線程過多會造成棧溢出著拭,也有可能會造成堆異常。
什么是重入鎖牍帚?
所謂重入鎖儡遮,指的是以線程為單位,當(dāng)一個線程獲取對象鎖之后暗赶,這個線程可以再次獲取本對象上的鎖鄙币,而其他的線程是不可以的。
線程 Thread.yield() 方法有什么用蹂随?
Yield 方法可以暫停當(dāng)前正在執(zhí)行的線程對象十嘿,讓其它有相同優(yōu)先級的線程執(zhí)行。它是一個靜態(tài)方法而且 只保證當(dāng)前線程放棄 CPU 占用岳锁,而不能保證使其它線程一定能占用 CPU绩衷,執(zhí)行 yield()
的線程有可能在進入到暫停狀態(tài)后馬上又被執(zhí)行。
什么是死鎖激率?
死鎖就是兩個線程相互等待對方釋放對象鎖咳燕。
為什么要使用線程池?
當(dāng)線程數(shù)達到一定數(shù)量就會耗盡系統(tǒng)的 CPU 和內(nèi)存資源乒躺,也會造成 GC 頻繁收集和停頓招盲,因為每次創(chuàng)建和銷毀一個線程都是要消耗系統(tǒng)資源的嘉冒,如果為每個任務(wù)都創(chuàng)建線程這無疑是一個很大的性能瓶頸顶籽。所以设拟,線程池中的線程復(fù)用極大節(jié)省了系統(tǒng)資源镰吆,當(dāng)線程一段時間不再有任務(wù)處理時它也會自動銷毀万皿,而不會長駐內(nèi)存牢硅。
什么是活鎖惩系?
活鎖是線程拿到對象鎖卻又相互釋放不執(zhí)行抒抬。當(dāng)多線程中出現(xiàn)了相互謙讓,都主動將對象鎖釋放給別的線程使用,這就是活鎖。
什么是饑餓?
優(yōu)先級高的線程能夠插隊并優(yōu)先執(zhí)行,這樣如果優(yōu)先級高的線程一直搶占資源骑素,導(dǎo)致低優(yōu)先級線程無法得到執(zhí)行末捣,與死鎖不同的如果占用資源的線程結(jié)束了并釋放了資源箩做,那么饑餓的線程還是有機會得到執(zhí)行的。
無鎖
無鎖,即沒有對資源進行鎖定,如果有多個線程修改同一個值必定會有一個線程能修改成功,而其他修改失敗的線程會通過不斷重試直到修改成功罗捎。 JDK 的 CAS 原理及應(yīng)用即是無鎖的實現(xiàn)观谦。可以看出桨菜,無鎖是一種非常良好的設(shè)計豁状,它不會出現(xiàn)線程出現(xiàn)的跳躍性問題,鎖使用不當(dāng)肯定會出現(xiàn)系統(tǒng)性能問題倒得,雖然無鎖無法全面代替有鎖泻红,但無鎖在某些場合下是非常高效的。
原子性
原子性是指一個線程的操作不能被其他線程打斷霞掺,同一時間只有一個線程對一個變量進行操作谊路。比如說多個線程同時對一個成員變量 n++
100次,線程之間的操作是互不干擾的菩彬,這就是傳說的中的原子性缠劝。但 n++ 并不是原子性的操作,要使用 AtomicInteger
保證原子性骗灶。
可見性
可見性是指某個線程修改了某一個共享變量的值惨恭,而其他線程是否可以看見該共享變量修改后的值。在單線程中肯定不會有這種問題耙旦,單線程讀到的肯定都是最新的值脱羡,而在多線程編程中就不一定了。每個線程都有自己的工作內(nèi)存,線程先把共享變量的值從主內(nèi)存讀到工作內(nèi)存锉罐,形成一個副本帆竹,當(dāng)計算完后再把副本的值刷回主內(nèi)存,從讀取到最后刷回主內(nèi)存這是一個過程氓鄙,當(dāng)還沒刷回主內(nèi)存的時候這時候?qū)ζ渌€程是不可見的馆揉,所以其他線程從主內(nèi)存讀到的值是修改之前的舊值业舍。像 CPU 的緩存優(yōu)化抖拦、硬件優(yōu)化、指令重排及對 JVM 編譯器的優(yōu)化舷暮,都會出現(xiàn)可見性的問題态罪。
有序性
我們都知道程序是按代碼順序執(zhí)行的,對于單線程來說確實是如此下面,但在多線程情況下就不是如此了复颈。為了優(yōu)化程序執(zhí)行和提高 CPU 的處理性能,JVM 和操作系統(tǒng)都會對指令進行重排沥割,也就說前面的代碼并不一定都會在后面的代碼前面執(zhí)行耗啦,即后面的代碼可能會插到前面的代碼之前執(zhí)行,只要不影響當(dāng)前線程的執(zhí)行結(jié)果机杜。所以帜讲,指令重排只會保證當(dāng)前線程執(zhí)行結(jié)果一致,但指令重排后勢必會影響多線程的執(zhí)行結(jié)果椒拗。雖然重排序優(yōu)化了性能似将,但也是會遵守一些規(guī)則的,并不能隨便亂排序蚀苛,只是重排序會影響多線程執(zhí)行的結(jié)果在验。
什么是守護線程?
與守護線程相對應(yīng)的就是用戶線程堵未,守護線程就是守護用戶線程腋舌,當(dāng)用戶線程全部執(zhí)行完結(jié)束之后,守護線程才會跟著結(jié)束渗蟹。也就是守護線程必須伴隨著用戶線程块饺,如果一個應(yīng)用內(nèi)只存在一個守護線程,沒有用戶線程拙徽,守護線程自然會退出刨沦。