前言
本來想著給自己放鬆一下,刷刷部落格,突然被幾道面試題難倒!常用的執行緒池有哪些?簡述一下你對執行緒池的理解?Java程式是如何執行的?鎖的最佳化機制瞭解嗎?說說程序和執行緒的區別?似乎有點模糊了,那就大概看一下面試題吧。好記性不如爛鍵盤
*** 12萬字的java面試題整理 ***
*** java核心面試知識整理 ***
*** Java高頻面試講解(知識涵蓋齊全) ***
常用的執行緒池有哪些?
- newSingleThreadExecutor:建立一個單執行緒的執行緒池,此執行緒池保證所有任務的執行順序按照任務的提交順序執行。
- newFixedThreadPool:建立固定大小的執行緒池,每次提交一個任務就建立一個執行緒,直到執行緒達到執行緒池的最大大小。
- newCachedThreadPool:建立一個可快取的執行緒池,此執行緒池不會對執行緒池大小做限制,執行緒池大小完全依賴於作業系統(或者說JVM)能夠建立的最大執行緒大小。
- newScheduledThreadPool:建立一個大小無限的執行緒池,此執行緒池支援定時以及週期性執行任務的需求。
- newSingleThreadExecutor:建立一個單執行緒的執行緒池。此執行緒池支援定時以及週期性執行任務的需求。
簡述一下你對執行緒池的理解
(如果問到了這樣的問題,可以展開的說一下執行緒池如何用、執行緒池的好處、執行緒池的啟動策略)合理利用執行緒池能夠帶來三個好處。
第一:降低資源消耗。透過重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗。
第二:提高響應速度。當任務到達時,任務可以不需要等到執行緒建立就能立即執行。
第三:提高執行緒的可管理性。執行緒是稀缺資源,如果無限制的建立,不僅會消耗系統資源,還會降低系統的穩定性,使用執行緒池可以進行統一的分配,調優和監控。
Java程式是如何執行的
我們日常的工作中都使用開發工具(IntelliJ IDEA 或 Eclipse 等)可以很方便的除錯程式,或者是透過打包工具把專案打包成 jar 包或者 war 包,放入 Tomcat 等 Web 容器中就可以正常執行了,但你有沒有想過 Java 程式內部是如何執行的?其實不論是在開發工具中執行還是在 Tomcat 中執行,Java 程式的執行流程基本都是相同的,它的執行流程如下:
- 先把 Java 程式碼編譯成位元組碼,也就是把 .java 型別的檔案編譯成 .class 型別的檔案。這個過程的大致執行流程:Java 原始碼 -> 詞法分析器 -> 語法分析器 -> 語義分析器 -> 字元碼生成器 ->最終生成位元組碼,其中任何一個節點執行失敗就會造成編譯失敗;
- 把 class 檔案放置到 Java 虛擬機器,這個虛擬機器通常指的是 Oracle 官方自帶的 Hotspot JVM;
- Java 虛擬機器使用類載入器(Class Loader)裝載 class 檔案;
- 類載入完成之後,會進行位元組碼效驗,位元組碼效驗透過之後 JVM 直譯器會把位元組碼翻譯成機器碼交由作業系統執行。但不是所有程式碼都是解釋執行的,JVM 對此做了最佳化,比如,以Hotspot 虛擬機器來說,它本身提供了 JIT(Just In Time)也就是我們通常所說的動態編譯器,它能夠在執行時將熱點程式碼編譯為機器碼,這個時候位元組碼就變成了編譯執行。Java 程式執行流程圖如下:
鎖的最佳化機制瞭解嗎?
從JDK1.6版本之後,synchronized本身也在不斷最佳化鎖的機制,有些情況下他並不會是一個很重量級的鎖了。最佳化機制包括自適應鎖、自旋鎖、鎖消除、鎖粗化、輕量級鎖和偏向鎖。
鎖的狀態從低到高依次為無鎖->偏向鎖->輕量級鎖->重量級鎖,升級的過程就是從低到高,降級在
一定條件也是有可能發生的。
- 自旋鎖:由於大部分時候,鎖被佔用的時間很短,共享變數的鎖定時間也很短,所有沒有必要掛起執行緒,使用者態和核心態的來回上下文切換嚴重影響效能。自旋的概念就是讓執行緒執行一個忙迴圈,可以理解為就是啥也不幹,防止從使用者態轉入核心態,自旋鎖可以透過設定-XX:+UseSpining來開啟,自旋的預設次數是10次,可以使用-XX:PreBlockSpin設定。
- 自適應鎖:自適應鎖就是自適應的自旋鎖,自旋的時間不是固定時間,而是由前一次在同一個鎖上的自旋時間和鎖的持有者狀態來決定。
- 鎖消除:鎖消除指的是JVM檢測到一些同步的程式碼塊,完全不存在資料競爭的場景,也就是不需要加鎖,就會進行鎖消除。
- 鎖粗化:鎖粗化指的是有很多操作都是對同一個物件進行加鎖,就會把鎖的同步範圍擴充套件到整個操作序列之外。
- 偏向鎖:當執行緒訪問同步塊獲取鎖時,會在物件頭和棧幀中的鎖記錄裡儲存偏向鎖的執行緒ID,之後這個執行緒再次進入同步塊時都不需要CAS來加鎖和解鎖了,偏向鎖會永遠偏向第一個獲得鎖的執行緒,如果後續沒有其他執行緒獲得過這個鎖,持有鎖的執行緒就永遠不需要進行同步,反之,當有其他執行緒競爭偏向鎖時,持有偏向鎖的執行緒就會釋放偏向鎖。可以用過設定-XX:+UseBiasedLocking開啟偏向鎖。
- 輕量級鎖:JVM的物件的物件頭中包含有一些鎖的標誌位,程式碼進入同步塊的時候,JVM將會使用CAS方式來嘗試獲取鎖,如果更新成功則會把物件頭中的狀態位標記為輕量級鎖,如果更新失敗,當前執行緒就嘗試自旋來獲得鎖。
整個鎖升級的過程非常複雜,我盡力去除一些無用的環節,簡單來描述整個升級的機制。
簡單點說,偏向鎖就是透過物件頭的偏向執行緒ID來對比,甚至都不需要CAS了,而輕量級鎖主要就是透過CAS修改物件頭鎖記錄和自旋來實現,重量級鎖則是除了擁有鎖的執行緒其他全部阻塞。
說說程序和執行緒的區別?
- 程序是一個“執行中的程式”,是系統進行資源分配和排程的一個獨立單位。
- 執行緒是程序的一個實體,一個程序中擁有多個執行緒,執行緒之間共享地址空間和其它資源(所以通訊和同步等操作執行緒比程序更加容易)
- 執行緒上下文的切換比程序上下文切換要快很多。
(1)程序切換時,涉及到當前程序的CPU環境的儲存和新被排程執行程序的CPU環境的設定。
(2)執行緒切換僅需要儲存和設定少量的暫存器內容,不涉及儲存管理方面的操作。