「BAT常問」40道Java基礎面試題及詳細答案整理彙總「17—24」

更多技術分享,請點擊右上角紅色的"關注",感謝你的支持!

本文為Java面試題系列文章第四篇,之前文章可點擊訪問:

一、

二、

三、

17.ConcurrentHashMap可以代替HashTable嗎?


hashTable雖然性能上比不上ConcurrentHashMap,但並不能完全被取代,兩者的迭代器的一致性不同的,hash table的迭代器是強一致性的,而concurrenthashmap是弱一致的。

ConcurrentHashMap的get,clear,iterator 都是弱一致性的。 Doug Lea(大神 Doug Lea是JCP 中的一員) 也將這個判斷留給用戶自己決定是否使用ConcurrentHashMap。

ConcurrentHashMap與HashTable都可以用於多線程的環境,但是當Hashtable的大小增加到一定的時候,性能會急劇下降,因為迭代時需要被鎖定很長的時間。因為ConcurrentHashMap引入了分割(segmentation),不論它變得多麼大,僅僅需要鎖定map的某個部分,而其它的線程不需要等到迭代完成才能訪問map。簡而言之,在迭代的過程中,ConcurrentHashMap僅僅鎖定map的某個部分,而Hashtable則會鎖定整個map。

那麼既然ConcurrentHashMap那麼優秀,為什麼還要有Hashtable的存在呢?ConcurrentHashMap能完全替代HashTable嗎?

HashTable雖然性能上不如ConcurrentHashMap,但並不能完全被取代,兩者的迭代器的一致性不同的,HashTable的迭代器是強一致性的,而ConcurrentHashMap是弱一致的。ConcurrentHashMap的get,clear,iterator 都是弱一致性的。

那麼什麼是強一致性和弱一致性呢?

get方法是弱一致的,是什麼含義?可能你期望往ConcurrentHashMap底層數據結構中加入一個元素後,立馬能對get可見,但ConcurrentHashMap並不能如你所願。換句話說,put操作將一個元素加入到底層數據結構後,get可能在某段時間內還看不到這個元素,若不考慮內存模型,單從代碼邏輯上來看,卻是應該可以看得到的。

我們結合java內存模型相關內容來分析下put/get方法。put方法我們只需關注Segment#put,get方法只需關注Segment#get,在繼續之前,先要說明一下Segment裡有兩個volatile變量:count和table;HashEntry裡有一個volatile變量:value。

總結

ConcurrentHashMap的弱一致性主要是為了提升效率,是一致性與效率之間的一種權衡。要成為強一致性,就得到處使用鎖,甚至是全局鎖,這就與Hashtable和同步的HashMap一樣了。

「BAT常問」40道Java基礎面試題及詳細答案整理彙總「17—24」

18.為什麼說HashMap是線程不安全的呢?


HashMap 在併發執行 put 操作時會引起死循環,導致 CPU 利用率接近100%。因為多線程會導致 HashMap 的 Node 鏈表形成環形數據結構,一旦形成環形數據結構,Node 的 next 節點永遠不為空,就會在獲取 Node 時產生死循環。

19.在使用HashMap時,如何做到線程安全?


瞭解了 HashMap 為什麼線程不安全,那現在看看如何線程安全的使用 HashMap。

這個無非就是以下三種方式:

Hashtable ConcurrentHashMap Synchronized Map

Hashtable例子代碼:

「BAT常問」40道Java基礎面試題及詳細答案整理彙總「17—24」

先稍微吐槽一下,為啥命名不是 HashTable 啊,看著好難受不管了就裝作它叫HashTable 吧。因為已經不常用了,就簡單說說吧。HashTable 源碼中是使用synchronized來保證線程安全的,比如下面的 get 方法和 put 方法:

「BAT常問」40道Java基礎面試題及詳細答案整理彙總「17—24」

所以當一個線程訪問 HashTable 的同步方法時,其他線程如果也要訪問同步方法,會被阻塞住。舉個例子,當一個線程使用 put 方法時,另一個線程不但不可以使用 put 方法,連 get 方法都不可以,這樣效率很低,現在基本不會使用它了。

ConcurrentHashMap

ConcurrentHashMap 於 Java 7 的,和8有區別,在8中 CHM 摒棄了 Segment(鎖段)的概念,而是啟用了一種全新的方式實現,利用 CAS 算法。

SynchronizedMap

synchronizedMap() 方法後會返回一個 SynchronizedMap 類的對象,而在 SynchronizedMap 類中使用了 synchronized 同步關鍵字來保證對 Map 的操作是線程安全的。

性能對比

這是要靠數據說話的時代,所以不能只靠嘴說 CHM 快,它就快了。寫個測試用例,實際的比較一下這三種方式的效率(源碼來源),下面的代碼分別通過三種方式創建 Map 對象,使用 ExecutorService 來併發運行5個線程,每個線程添加/獲取500K個元素。

「BAT常問」40道Java基礎面試題及詳細答案整理彙總「17—24」

ConcurrentHashMap 性能是明顯優於 Hashtable 和 SynchronizedMap 的,CHM 花費的時間比前兩個的一半還少。

20.多併發情況下HashMap是否還會產生死循環?


看下了ConcurrentHashMap的源碼,ConcurrentHashMap是Java 5中支持高併發、高吞吐量的線程安全HashMap實現。

在看很多博客在介紹ConcurrentHashMap之前,都說HashMap適用於單線程訪問,這是因為HashMap的所有方法都沒有進行鎖同步,因此是線程不安全的,不僅如此,當多線程訪問的時候還容易產生死循環。

建議參考博客文章《Java困惑》:多併發情況下HashMap是否還會產生死循環。講的很詳細,百度很好找到。

既然會產生死循環,為什麼併發情況下,還是用ConcurrentHashMap。 jdk 好像有,但是Jdk8 已經修復了這個問題。

21.講一講TreeMap、HashMap、LindedHashMap的區別


LinkedHashMap可以保證HashMap集合有序,存入的順序和取出的順序一致。

TreeMap實現SortMap接口,能夠把它保存的記錄根據鍵排序,默認是按鍵值的升序排序,也可以指定排序的比較器,當用Iterator遍歷TreeMap時,得到的記錄是排過序的。

HashMap不保證順序,即為無序的,具有很快的訪問速度。HashMap最多隻允許一條記錄的鍵為Null;允許多條記錄的值為 Null。HashMap不支持線程的同步。

我們在開發的過程中使用HashMap比較多,在Map中在Map 中插入、刪除和定位元素,HashMap 是最好的選擇

但如果您要按自然順序或自定義順序遍歷鍵,那麼TreeMap會更好

如果需要輸出的順序和輸入的相同,那麼用LinkedHashMap 可以實現,它還可以按讀取順序來排列

「BAT常問」40道Java基礎面試題及詳細答案整理彙總「17—24」

22.說說Collection包的結構,和Collections的區別在哪裡?


Collection 是集合類的上級接口,子接口主要有Set、List 、Map。

Collecions 是針對集合類的一個幫助類, 提供了操作集合的工具方法,一系列靜態方法實現對各種集合的搜索、排序線性、線程安全化等操作。

「BAT常問」40道Java基礎面試題及詳細答案整理彙總「17—24」

Collection

Collection 是單列集合

List

元素是有序的、可重複。有序的 collection,可以對列表中每個元素的插入位置進行精確地控制。可以根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素。可存放重複元素,元素存取是有序的。

List接口中常用類

Vector:線程安全,但速度慢,已被ArrayList替代。底層數據結構是數組結構。ArrayList:線程不安全,查詢速度快。底層數據結構是數組結構。LinkedList:線程不安全。增刪速度快。底層數據結構是列表結構。

Set

Set接口中常用的類

Set(集) 元素無序的、不可重複。取出元素的方法只有迭代器。不可以存放重複元素,元素存取是無序的。

HashSet:線程不安全,存取速度快。它是如何保證元素唯一性的呢?依賴的是元素的hashCode方法和euqals方法。TreeSet:線程不安全,可以對Set集合中的元素進行排序。它的排序是如何進行的呢?通過compareTo或者compare方法中的來保證元素的唯一性。元素是以二叉樹的形式存放的。

Map

map是一個雙列集合

Hashtable:線程安全,速度快。底層是哈希表數據結構。是同步的。不允許null作為鍵,null作為值。

Properties:用於配置文件的定義和操作,使用頻率非常高,同時鍵和值都是字符串。是集合中可以和IO技術相結合的對象。

HashMap:線程不安全,速度慢。底層也是哈希表數據結構。是不同步的。允許null作為鍵,null作為值,替代了Hashtable。

LinkedHashMap: 可以保證HashMap集合有序。存入的順序和取出的順序一致。

TreeMap:可以用來對Map集合中的鍵進行排序

23.用到try catch finally時,如果try裡有return,finally裡邊的代碼還執行麼?


肯定會執行。finally{}塊的代碼。只有在try{}塊中包含遇到System.exit(0)。之類的導致Java虛擬機直接退出的語句才會不執行。

當程序執行try{}遇到return時,程序會先執行return語句,但並不會立即返回——也就是把return語句要做的一切事情都準備好,也就是在將要返回、但並未返回的時候,程序把執行流程轉去執行finally塊,當finally塊執行完成後就直接返回剛才return語句已經準備好的結果。

「BAT常問」40道Java基礎面試題及詳細答案整理彙總「17—24」

24.說說Excption與Error包結構,OOM你遇到過哪些情況?SOF你遇到過哪些情況?


Throwable是 Java 語言中所有錯誤或異常的超類。Throwable包含兩個子類: Error 和 Exception 。它們通常用於指示發生了異常情況。Throwable包含了其線程創建時線程執行堆棧的快照,它提供了printStackTrace()等接口用於獲取堆棧跟蹤數據等信息。

Java將可拋出(Throwable)的結構分為三種類型:

被檢查的異常(Checked Exception)。

運行時異常(RuntimeException)。

錯誤(Error)。

運行時異常RuntimeException

定義 : RuntimeException及其子類都被稱為運行時異常。特點 : Java編譯器不會檢查它 也就是說,當程序中可能出現這類異常時,倘若既"沒有通過throws聲明拋出它",也"沒有用try-catch語句捕獲它",還是會編譯通過。

例如,除數為零時產生的ArithmeticException異常,數組越界時產生的IndexOutOfBoundsException異常,fail-fail機制產生的ConcurrentModificationException異常等,都屬於運行時異常。

堆內存溢出 OutOfMemoryError(OOM)

除了程序計數器外,虛擬機內存的其他幾個運行時區域都有發生OutOfMemoryError(OOM)異常的可能。

Java Heap 溢出。一般的異常信息:java.lang.OutOfMemoryError:Java heap spacess。java堆用於存儲對象實例,我們只要不斷的創建對象,並且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,就會在對象數量達到最大堆容量限制後產生內存溢出異常。

堆棧溢出 StackOverflow (SOF)

StackOverflowError 的定義:當應用程序遞歸太深而發生堆棧溢出時,拋出該錯誤。因為棧一般默認為1-2m,一旦出現死循環或者是大量的遞歸調用,在不斷的壓棧過程中,造成棧容量超過1m而導致溢出。

棧溢出的原因

1、遞歸調用

2、大量循環或死循環

3、全局變量是否過多

4、數組、List、map數據過大

「BAT常問」40道Java基礎面試題及詳細答案整理彙總「17—24」


分享到:


相關文章: