跳槽不算頻繁,但參加過很多面試(電話面試、face to face 面試),面過大 / 小公司、互聯網 / 傳統軟件公司,麪糊過(眼高手低,缺少實戰經驗,掛掉),也面過人,所幸未因失敗而氣餒,在此過程當中不斷查缺補漏,養成了踏實、追本溯源、持續改進的習慣,特此將本身經歷過、構思過的一些面試題記錄下來,若是答案有問題,歡迎拍磚討論,但願能對找工做或者感興趣的同窗有所幫助,陸續整理中。java
1. synchronized 和 reentrantlock 異同
相同點web
不一樣點面試
- 實現機制不一樣 synchronized 經過 java 對象頭鎖標記和 Monitor 對象實現 reentrantlock 經過
CAS、ASQ(AbstractQueuedSynchronizer)和 locksupport(用於阻塞和解除阻塞)實現
synchronized 依賴 jvm 內存模型保證包含共享變量的多線程內存可見性 reentrantlock 經過 ASQ 的
volatile state 保證包含共享變量的多線程內存可見性
- 使用方式不一樣 synchronized 能夠修飾實例方法(鎖住實例對象)、靜態方法(鎖住類對象)、代碼塊(顯示指定鎖對象)reentrantlock 顯示調用 trylock()/lock() 方法,須要在 finally 塊中釋放鎖
- 功能豐富程度不一樣 reentrantlock
提供有限時間等候鎖(設置過時時間)、可中斷鎖(lockInterruptibly)、condition(提供 await、signal
等方法)等豐富語義 reentrantlock 提供公平鎖和非公平鎖實現 synchronized
不可設置等待時間、不可被中斷(interrupted)
2. concurrenthashmap 爲什麼讀不用加鎖
jdk1.7數據庫
- 1)HashEntry 中的 key、hash、next 均爲 final 型,只能表頭插入、刪除結點
- 2)HashEntry 類的 value 域被聲明爲 volatile 型
- 3)不容許用 null 做爲鍵和值,當讀線程讀到某個 HashEntry 的 value 域的值爲 null
時,便知道產生了衝突——發生了重排序現象(put 設置新 value 對象的字節碼指令重排序),須要加鎖後從新讀入這個 value 值
- 4)volatile 變量 count 協調讀寫線程之間的內存可見性,寫操做後修改 count,讀操做先讀 count,根據
happen-before 傳遞性原則寫操做的修改讀操做可以看到
jdk1.8編程
- 1)Node 的 val 和 next 均爲 volatile 型
- 2)tabAt 和 casTabAt 對應的 unsafe 操做實現了 volatile 語義
3. ContextClassLoader(線程上下文類加載器)的做用
- 越過類加載器的雙親委派機制去加載類,如 serviceloader 實現
- 使用線程上下文類加載器加載類,要注意保證多個須要通訊的線程間的類加載器應該是同一個,防止由於不一樣的類加載器致使類型轉換異常(ClassCastException)
4. tomcat 類加載機制

- 不一樣應用使用不一樣的 webapp 類加載器,實現應用隔離的效果,webapp 類加載器下面是 jsp 類加載器
- 不一樣應用共享的 jar 包能夠放到 Shared 類加載器 /shared 目錄下
5. osgi 類加載機制

- osgi 類加載模型是網狀的,能夠在模塊(Bundle)間互相委託
- osgi 實現模塊化熱部署的關鍵是自定義類加載器機制的實現,每一個 Bundle 都有一個本身的類加載器,當須要更換一個 Bundle時,就把 Bundle 連同類加載器一塊兒換掉以實現代碼的熱替換
當收到類加載請求時,osgi 將按照下面的順序進行類搜索:數組
- 1)將以 java.* 開頭的類委派給父類加載器加載
- 2)不然,將委派列表名單(配置文件 org.osgi.framework.bootdelegation 中定義)內的類委派給父類加載器加載
- 3)不然,檢查是否在 Import-Package 中聲明,若是是,則委派給 Export 這個類的 Bundle 的類加載器加載
- 4)不然,檢查是否在 Require-Bundle 中聲明,若是是,則將類加載請求委託給 required bundle 的類加載器
- 5)不然,查找當前 Bundle 的 ClassPath,使用本身的類加載器加載
- 6)不然,查找類是否在本身的 Fragment Bundle 中,若是在,則委派給 Fragment Bundle 的類加載器加載
- 7)不然,查找 Dynamic Import-Package(Dynamic Import 只有在真正用到此 Package
的時候才進行加載)的 Bundle,委派給對應 Bundle 的類加載器加載
- 8)不然,類查找失敗
6. 如何結束一個一直運行的線程
- 使用退出標誌,這個 flag 變量要多線程可見
- 使用 interrupt,結合 isInterrupted() 使用
7. threadlocal 使用場景及問題
- threadlocal 並不能解決多線程共享變量的問題,同一個 threadlocal 所包含的對象,在不一樣的 thread
中有不一樣的副本,互不干擾
- 用於存放線程上下文變量,方便同一線程對變量的先後屢次讀取,如事務、數據庫 connection 鏈接,在 web 編程中使用的更多
- 問題: 注意線程池場景使用 threadlocal,由於實際變量值存放在了 thread 的 threadlocalmap
類型變量中,若是該值沒有 remove,也沒有先 set 的話,可能會獲得之前的舊值
- 問題: 注意線程池場景下的內存泄露,雖然 threadlocal 的 get/set 會清除 key(key 爲 threadlocal的弱引用,value 是強引用,致使 value 不釋放)爲 null 的 entry,可是最好 remove
8. 線程池從啓動到工做的流程
剛建立時,裏面沒有線程
調用 execute() 添加任務時:tomcat
- 1)若是正在運行的線程數量小於核心參數 corePoolSize,繼續建立線程運行這個任務
- 2)不然,若是正在運行的線程數量大於或等於 corePoolSize,將任務加入到阻塞隊列中
- 3)不然,若是隊列已滿,同時正在運行的線程數量小於核心參數 maximumPoolSize,繼續建立線程運行這個任務
- 4)不然,若是隊列已滿,同時正在運行的線程數量大於或等於 maximumPoolSize,根據設置的拒絕策略處理
- 5)完成一個任務,繼續取下一個任務處理
- 6)沒有任務繼續處理,線程被中斷或者線程池被關閉時,線程退出執行,若是線程池被關閉,線程結束
- 7)不然,判斷線程池正在運行的線程數量是否大於核心線程數,若是是,線程結束,不然線程阻塞。所以線程池任務所有執行完成後,繼續留存的線程池大小爲corePoolSize
- 8)本文所列出的 14 個 Java面試題只是我所遭遇的面試中的一部分,其餘的面試題我也會陸續整理出來,說到這裏另外順便給你們推薦一個架構交流學習羣:650385180,裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty 源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化這些成爲架構師必備的知識體系。還能領取免費的學習資源,相信對於已經工做和遇到技術瓶頸的碼友,在這個羣裏會有你須要的內容。
9. 阻塞隊列 BlockingQueue take 和 poll 區別
- poll(time):取走 BlockingQueue 裏排在首位的對象, 若不能當即取出,則能夠等 time參數規定的時間,取不到時返回 null
- take():取走 BlockingQueue 裏排在首位的對象,若 BlockingQueue 爲空,阻塞直到BlockingQueue 有新的對象被加入
10. 如何從 FutureTask 不阻塞獲取結果
- get(long timeout,TimeUnit unit),超時則返回
- 輪詢,先經過 isDone()判斷是否結束,而後調用 get()
11. blockingqueue 若是存放了比較關鍵的數據,系統宕機該如何處理
- 開放性問題,歡迎討論
- 將隊列持久化,比較麻煩,須要將生產數據持久化到磁盤,持久化成功才返回,消費者線程從磁盤加載數據到內存阻塞隊列中,維護消費offset,啓動時,根據消費 offset 從磁盤加載數據
- 加入消息隊列,保證消息不丟失,生成序列號,消費冪等,根據消費進程決定系統重啓後的生產狀態
12. NIO 與傳統 I/O 的區別
- 節約線程,NIO 由原來的每一個線程都須要阻塞讀寫變成了由單線程(即 Selector)負責處理多個 channel
註冊(register)的興趣事件(SelectionKey)集合(底層藉助操做系統提供的 epoll()),netty bossgroup 處理 accept 鏈接(沒看明白爲何 bossgroup 設置多個 thread的必要性),workergroup 處理具體業務流程和數據讀寫
- NIO 提供非阻塞操做
- 傳統 I/O 以流的方式處理數據,而 NIO 以塊的方式處理數據,NIO 提供 bytebuffer,分爲堆內和堆外緩衝區,讀寫時均先放到該緩衝區中,而後由內核經過 channel傳輸到對端,堆外緩衝區不走內核,提高了性能
13. list 中存放可重複字符串,如何刪除某個字符串
- 調用 iterator 相關方法刪除
- 倒刪,防止正序刪除致使的數組重排,index 跳過數組元素問題
14. 有哪些 GC ROOTS(跟平常開發比較相關的是和此相關的內存泄露)
- 全部 Java 線程當前活躍的棧幀裏指向 GC 堆裏的對象的引用,所以用不到的對象及時置 null,提高內存回收效率
- 靜態變量引用的對象,所以減小靜態變量特別是靜態集合變量的大小,集合存放的對象覆寫 euqls()和 hashcode(),防止持續增加
- 本地方法 JNI 引用的對象
- 方法區中的常量引用的對象,所以減小在長字符串上調用 String.intern()
- classloader 加載的 class 對象,所以自定義 classloader 無效時及時置 null而且注意類加載器加載對象之間的隔離
- jvm 裏的一些靜態數據結構裏指向 GC 堆裏的對象的引用
- …