自己并不是 CS 科班出身,學習 Java 的時間也不長,但自己比較喜歡這行業(yè)。所以想寫一些文章記錄一些自己想去詳細了解的知識,并且希望能分享出來大家進行討論,一起進步~
我比較喜歡問題驅(qū)動學習的形式,所以經(jīng)常會看一些大公司的面試題了解自己的不足,下面有五道面試題與大家討論。下面討論的問題出自 —— 你應該知道的JAVA面試題
1. Java 線程的狀態(tài)
這算是一個比較常見的問題了,經(jīng)常在不同的面經(jīng)里看見,但經(jīng)常又記了大概就忘記。
Java 線程在某個時刻只能處于以下六種狀態(tài)
- 新建(NEW),線程尚未啟動的狀態(tài)。
- 可運行(RUNNABLE),可運行狀態(tài)的線程正在Java虛擬機中執(zhí)行,但它可能正在等待來自操作系統(tǒng)(例如處理器)的其他資源。
- 阻塞中(BLOCKED),一個線程試圖獲取監(jiān)視器鎖(JVM規(guī)范實現(xiàn)的內(nèi)容,每個對象和類在邏輯上都是和一個監(jiān)視器相關(guān)聯(lián)的),但該鎖正在被其它線程持有時的狀態(tài)。例如進入Synchronize塊或者其它線程線程調(diào)用了Object.wait方法。
- 等待(WAITING),一個線程調(diào)用了 Object.wait、Thread.join或者LockSupport.park后的狀態(tài)。例如一個線程調(diào)用了 Object.wait 等待 Object.notify 或者 Object.notifyAll 方法。
- 計時等待(TIMED_WAITING),就是觸發(fā)等待狀態(tài)的相關(guān)方法加上時間參數(shù)的狀態(tài)。
- 終止(TERMINATED),線程被終止(拋出一個未被捕獲的異常)或正常退出的狀態(tài)。
參考資料:JDK8 的 Thread 源碼,相關(guān)源碼在1742行開始

2. 進程和線程的區(qū)別,進程間如何通訊,線程間如何通訊
我也沒上過操作系統(tǒng)的課,只能基于搜索的資料與自己的理解和大家討論。
進程和線程都是基于一個 CPU 時間段的描述,線程是基于進程的進一步劃分。現(xiàn)代 OS 中,進程是 CPU 資源分配的最小單位,線程是 CPU 調(diào)度的最小單位。進程之前通訊依靠系統(tǒng)資源,線程之間通訊依靠 JVM 提供的方法。
用戶下達運行程序的命令后,就會產(chǎn)生進程。同一程序可產(chǎn)生多個進程(一對多關(guān)系),以允許同時有多位用戶運行同一程序,卻不會相沖突。
維基百科相關(guān)解釋:進程需要一些資源才能完成工作,如CPU使用時間、內(nèi)存、文件以及I/O設備,且為依序逐一進行,也就是每個CPU核心任何時間內(nèi)僅能運行一項進程。
那基于多線程能依賴于多核心執(zhí)行,就是說多個核心能同時運行一個進程?
進程與線程似乎還與操作系統(tǒng),語言實現(xiàn)有關(guān),并沒有找到很好的資料,歡迎大家討論。

3. HashMap 的數(shù)據(jù)結(jié)構(gòu)是什么?如何實現(xiàn)的。和 HashTable,ConcurrentHashMap 的區(qū)別
HashMap 是基于 Node 為元素的數(shù)組,Node 實現(xiàn)了 Map.Entry 接口,實現(xiàn)上來說有幾個關(guān)鍵的點
- put時計算出相應的hash值映射到相應的桶中。
- 如果一個桶中有多個元素則將該通內(nèi)部元素構(gòu)造成鏈表提高索引效率。
- 如果內(nèi)部鏈表長度超過 TREEIFY_THRESHOLD 的值(默認為8)將其轉(zhuǎn)換成紅黑樹,如果紅黑樹內(nèi)元素小于 UNTREEIFY_THRESHOLD 大小時將其轉(zhuǎn)換為鏈表。
- HashMap 默認的初始容量(DEFAULT_INITIAL_CAPACITY)為16,默認的負載因子(DEFAULT_LOAD_FACTOR)為0.75。
- 負載因子表示 HashMap 對表空間的使用程度,負載因子高時表內(nèi)空間就越緊湊,檢索效率就低,占用空間就相對來說小。
- 當容量不足時會出發(fā) resize 方法在達到最大值之前會默認擴大兩倍,達到最大值就不進行擴容。
- HashMap 與 HashTable 的哈希算法不同,HashMap 為了加快哈希速度,將哈希表的大小固定為 2 的冪,而 HashTable 使用取模運算實現(xiàn)。簡單的取模哈希的結(jié)果會更加均勻,所以 HashTable 會分散的更均勻,但由于取模運算對比位運算效率較低。HashMap 又實現(xiàn)了拉鏈法+紅黑樹的實現(xiàn),綜上我認為 HashMap更好。

Hashtable 是遺留類,很多映射的常用功能與 HashMap 類似,不同的是它承自 Dictionary 類,并且是線程安全的,任一時間只有一個線程能寫 Hashtable,并發(fā)性不如 ConcurrentHashMap,因為 ConcurrentHashMap 引入了分段鎖。Hashtable 不建議在新代碼中使用,不需要線程安全的場合可以用 HashMap 替換,需要線程安全的場合可以用 ConcurrentHashMap 替換。
ConcurrentHashMap 大量的使用了 Unsafe 的本地方法,使用分段鎖,降低了鎖粒度,提高了在競爭激烈的情況下的性能。Guava 的緩存就是參考其實現(xiàn)的。
參考資料:
https://tech.meituan.com/java-hashmap.html
http://www.infoq.com/cn/articles/ConcurrentHashMap/
https://my.oschina.net/ovirtKg/blog/777520
4. 索引有什么用?如何建索引?
索引相當于目錄的功能,例如我們翻書時想快速找到自己感興趣的章節(jié)就要通過索引。如果沒有索引時我們只能一頁一頁的去找自己想要的內(nèi)容。不同的數(shù)據(jù)庫實現(xiàn)的索引不同,MySQL 中常用的有 B-TREE(從技術(shù)上說是B+TREE) 和哈希索引。
就我個人經(jīng)驗來說,創(chuàng)建索引時我會使用 explain 調(diào)試和業(yè)務相關(guān)的語句,當讀寫都在一個數(shù)據(jù)庫中時要少建索引,避免插入效率低,因為插入時索引自然要去更新一遍,當索引太多時更新就會對整個流程有影響。
避免冗余的索引,例如 AB 索引就維護了 A 的索引,了解 B+TREE 的結(jié)構(gòu)很直觀的就能理解,MySQL會針對搜索做優(yōu)化,但使用索引的效率低時就選擇不用索引。
選擇合適的索引順序,原則上說選擇性高的索引應放在前面,但又要考慮索引利用率的問題。當然如果實現(xiàn)了讀寫分離,那就可以隨意建索引了!

參考資料:高性能mysql第三版
5. ArrayList 是如何實現(xiàn)的,ArrayList 和 LinkedList 的區(qū)別?ArrayList 如何實現(xiàn)擴容?
ArrayList 是基于動態(tài)數(shù)組實現(xiàn)的數(shù)據(jù)結(jié)構(gòu),LinkedList 是基于雙向鏈表實現(xiàn)的數(shù)據(jù)結(jié)構(gòu)。所以 ArrayList 查找更快,LinkedList 占用的空間更大。由于 LinkedList 是基于鏈表實現(xiàn)的,而 ArrayList 在插入時又需要動態(tài)擴容,所以插入和刪除應該理論上比 ArrayList 更快。但實際測試結(jié)果并不是如此,由于 LinkedList 的檢索效率太拙計,只要在容量大小的 1/10 之前會快于 ArrayList。

參考資料:
http://blog.csdn.net/eson_15/article/details/51145788
http://www.importnew.com/6629.html