前言
本來想著給自己放松一下,刷刷博客,突然被幾道面試題難倒!常用的線程池有哪些?簡述一下你對線程池的理解?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 程序執行流程圖如下:
9.png
鎖的優化機制了解嗎?
從JDK1.6版本之后,synchronized本身也在不斷優化鎖的機制,有些情況下他并不會是一個很重量級的鎖了。優化機制包括自適應鎖、自旋鎖、鎖消除、鎖粗化、輕量級鎖和偏向鎖。
鎖的狀態從低到高依次為無鎖->偏向鎖->輕量級鎖->重量級鎖,升級的過程就是從低到高,降級在
一定條件也是有可能發生的。
- 自旋鎖:由于大部分時候,鎖被占用的時間很短,共享變量的鎖定時間也很短,所有沒有必要掛起線程,用戶態和內核態的來回上下文切換嚴重影響性能。自旋的概念就是讓線程執行一個忙循環,可以理解為就是啥也不干,防止從用戶態轉入內核態,自旋鎖可以通過設置-XX:+UseSpining來開啟,自旋的默認次數是10次,可以使用-XX:PreBlockSpin設置。
- 自適應鎖:自適應鎖就是自適應的自旋鎖,自旋的時間不是固定時間,而是由前一次在同一個鎖上的自旋時間和鎖的持有者狀態來決定。
- 鎖消除:鎖消除指的是JVM檢測到一些同步的代碼塊,完全不存在數據競爭的場景,也就是不需要加鎖,就會進行鎖消除。
- 鎖粗化:鎖粗化指的是有很多操作都是對同一個對象進行加鎖,就會把鎖的同步范圍擴展到整個操作序列之外。
- 偏向鎖:當線程訪問同步塊獲取鎖時,會在對象頭和棧幀中的鎖記錄里存儲偏向鎖的線程ID,之后這個線程再次進入同步塊時都不需要CAS來加鎖和解鎖了,偏向鎖會永遠偏向第一個獲得鎖的線程,如果后續沒有其他線程獲得過這個鎖,持有鎖的線程就永遠不需要進行同步,反之,當有其他線程競爭偏向鎖時,持有偏向鎖的線程就會釋放偏向鎖。可以用過設置-XX:+UseBiasedLocking開啟偏向鎖。
- 輕量級鎖:JVM的對象的對象頭中包含有一些鎖的標志位,代碼進入同步塊的時候,JVM將會使用CAS方式來嘗試獲取鎖,如果更新成功則會把對象頭中的狀態位標記為輕量級鎖,如果更新失敗,當前線程就嘗試自旋來獲得鎖。
整個鎖升級的過程非常復雜,我盡力去除一些無用的環節,簡單來描述整個升級的機制。
簡單點說,偏向鎖就是通過對象頭的偏向線程ID來對比,甚至都不需要CAS了,而輕量級鎖主要就是通過CAS修改對象頭鎖記錄和自旋來實現,重量級鎖則是除了擁有鎖的線程其他全部阻塞。
說說進程和線程的區別?
- 進程是一個“執行中的程序”,是系統進行資源分配和調度的一個獨立單位。
- 線程是進程的一個實體,一個進程中擁有多個線程,線程之間共享地址空間和其它資源(所以通信和同步等操作線程比進程更加容易)
- 線程上下文的切換比進程上下文切換要快很多。
(1)進程切換時,涉及到當前進程的CPU環境的保存和新被調度運行進程的CPU環境的設置。
(2)線程切換僅需要保存和設置少量的寄存器內容,不涉及存儲管理方面的操作。