面試題

常見的集合?

Map接口和Collection接口是全部集合框架的父接口:java

  1. Collection接口的子接口包括:Set接口和List接口
  2. Map接口的實現類主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
  3. Set接口的實現類主要有:HashSet、TreeSet、LinkedHashSet等
  4. List接口的實現類主要有:ArrayList、LinkedList、Stack以及Vector等

fail-fast 機制

fail-fast 機制是java集合(Collection)中的一種錯誤機制。當多個線程對同一個集合的內容進行操做時,就可能會產生fail-fast事件。例如:當某一個線程A經過iterator去遍歷某集合的過程當中,若該集合的內容被其餘線程所改變了;那麼線程A訪問集合時,就會拋出ConcurrentModificationException異常。咱們知道,對於集合如list,map類,咱們均可以經過迭代器來遍歷,而Iterator其實只是一個接口,具體的實現仍是要看具體的集合類中的內部類去實現Iterator並實現相關方法。spring

在ArrayList中,當調用list.iterator()時,其源碼是:public    Iterator<E>   iterator(){  return   new Itr();  }docker

在ArrayList的內部類Itr中,有屬性:int expectedModCount = ArrayList.this.modCount;數據庫

它是fail-fast判斷的關鍵變量了,它初始值就爲ArrayList中的modCount。modCount是抽象類AbstractList中的變量,用於記錄集合操做過程當中做的修改次數,默認爲0,ArrayList 繼承AbstractList。數組

每次調用next()方法,在實際訪問元素前,都會調用checkForComodification方法,該方法源碼以下:瀏覽器

final void checkForComodification() {
  if (modCount != expectedModCount)
  throw new ConcurrentModificationException();
}
在該段代碼中,當modCount != expectedModCount時,就會拋出該異常。可是在一開始的時候,expectedModCount初始值默認等於modCount,爲何會出現modCount != expectedModCount,很明顯expectedModCount在整個迭代過程除了一開始賦予初始值modCount外,並無再發生改變,因此可能發生改變的就只有modCount,在前面關於ArrayList擴容機制的分析中,能夠知道在ArrayList進行add,remove,clear等涉及到修改集合中的元素個數的操做時,modCount就會發生改變(modCount ++),因此當另外一個線程(併發修改)或者同一個線程遍歷過程當中,調用相關方法使集合的個數發生改變,就會使modCount發生變化,這樣在checkForComodification方法中就會拋出ConcurrentModificationException異常。
相似的,hashMap中發生的原理也是同樣的。tomcat

ArrayList 和 Vector 的區別?

這兩個類都實現了 List 接口(List 接口繼承了 Collection 接口),他們都是有序集合,即存儲在這兩個集合中的元素位置都是有順序的,至關於一種動態的數組,咱們之後能夠按位置索引來取出某個元素,而且其中的數據是容許重複的,這是與 HashSet 之類的集合的最大不一樣處,HashSet 之類的集合不能夠按索引號去檢索其中的元素,也不容許有重複的元素。ArrayList 與 Vector 的區別主要包括兩個方面:安全

  1. 同步性: Vector 是線程安全的,也就是說它的方法之間是線程同步(加了synchronized 關鍵字)的,而 ArrayList 是線程不安全的,它的方法之間是線程不一樣步的。若是隻有一個線程會訪問到集合,那最好是使用 ArrayList,由於它不考慮線程安全的問題,因此效率會高一些;若是有多個線程會訪問到集合,那最好是使用 Vector,由於不須要咱們本身再去考慮和編寫線程安全的代碼。
  2. 數據增加: ArrayList 與 Vector 都有一個初始的容量大小,當存儲進它們裏面的元素的我的超過了容量時,就須要增長 ArrayList 和 Vector 的存儲空間,每次要增長存儲空間時,不是隻增長一個存儲單元,而是增長多個存儲單元,每次增長的存儲單元的個數在內存空間利用與程序效率之間要去的必定的平衡。Vector 在數據滿時(加載因子1)增加爲原來的兩倍(擴容增量:原容量的 2 倍),而 ArrayList 在數據量達到容量的一半時(加載因子 0.5)增加爲原容量的 (0.5 倍 + 1) 個空間。

ArrayList和LinkedList的區別?

  1. LinkedList 實現了 List 和 Deque 接口,通常稱爲雙向鏈表;ArrayList 實現了 List 接口,動態數組;
  2. LinkedList 在插入和刪除數據時效率更高,ArrayList 在查找某個 index 的數據時效率更高;
  3. LinkedList 比 ArrayList 須要更多的內存;

Array 和 ArrayList 有什麼區別?何時該應 Array 而不是 ArrayList 呢?

  1. Array 能夠包含基本類型和對象類型,ArrayList 只能包含對象類型。
  2. Array 大小是固定的,ArrayList 的大小是動態變化的。
  3. ArrayList 提供了更多的方法和特性,好比:addAll(),removeAll(),iterator() 等等。

對於基本類型數據,集合使用自動裝箱來減小編碼工做量。可是,當處理固定大小的基本數據類型的時候,這種方式相對比較慢。服務器

Arraylist 的動態擴容機制

當在 ArrayList 中增長一個對象時 Java 會去檢查 Arraylist 以確保已存在的數組中有足夠的容量來存儲這個新對象(默認爲 10,最大容量爲 int 上限),若是沒有足夠容量就新建一個長度更長的數組(原來的1.5倍),舊的數組就會使用 Arrays.copyOf 方法被複制到新的數組中去,現有的數組引用指向了新的數組mybatis

Runnable接口和Callable接口的區別

Runnable接口中的run()方法的返回值是void,它只是純粹地去執行run()方法中的代碼而已;

Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合能夠用來獲取異步執行的結果

多線程有什麼用

  1. 發揮多核CPU的優點:若是是單線程的程序,那麼在雙核CPU上就浪費了50%,在4核CPU上就浪費了75%。多線程能夠充分利用CPU的。
  2. 防止阻塞:多條線程同時運行,一條線程的代碼執行阻塞,也不會影響其它任務的執行。

wait和sleep

  1. wait是Object的方法;sleep是Thread靜態方法
  2. wait是要配合synchronized配合使用的(必定放在synchronized裏面),調用後線程阻塞,被阻塞的線程只能由notify或notifyAll方法喚醒
  3. sleep只是將線程睡眠,讓其餘線程先執行,等待時間到天然甦醒,從新回到可運行狀態等待調度
  4. 最重要區別,sleep睡眠的時候,是不會釋放已持有的鎖的,而wait釋放鎖

Callable和Future?

Callable 和 Future 是比較有趣的一對組合。當咱們須要獲取線程的執行結果時,就須要用到它們。Callable用於產生結果,Future用於獲取結果。

Callable接口使用泛型去定義它的返回類型。Executors類提供了一些有用的方法去在線程池中執行Callable內的任務。因爲Callable任務是並行的,必須等待它返回的結果。java.util.concurrent.Future對象解決了這個問題。

在線程池提交Callable任務後返回了一個Future對象,使用它能夠知道Callable任務的狀態和獲得Callable返回的執行結果。Future提供了get()方法,等待Callable結束並獲取它的執行結果。

代碼示例

Callable 是一個接口,它只包含一個call()方法。Callable是一個返回結果而且可能拋出異常的任務。

爲了便於理解,咱們能夠將Callable比做一個Runnable接口,而Callable的call()方法則相似於Runnable的run()方法。

 

什麼是樂觀鎖和悲觀鎖?

悲觀鎖:悲觀,認爲其餘線程會修改變量,Java在JDK1.5以前都是靠synchronized關鍵字保證同步的,這種經過使用一致的鎖定協議來協調對共享狀態的訪問,能夠確保不管哪一個線程持有共享變量的鎖,都採用獨佔的方式來訪問這些變量。獨佔鎖其實就是一種悲觀鎖,因此能夠說synchronized是悲觀鎖。

樂觀鎖:樂觀,認爲其餘線程不必定會修改變量,樂觀鎖( Optimistic Locking)實際上是一種思想。相對悲觀鎖而言,樂觀鎖假設認爲數據通常狀況下不會形成衝突,因此在數據進行提交更新的時候,纔會正式對數據的衝突與否進行檢測,若是發現衝突了,則讓返回用戶錯誤的信息,讓用戶決定如何去作。

Java中的方法覆蓋(Overriding)和方法重載(Overloading)是什麼意思?

Java的方法重載,就是在類中能夠建立多個方法,它們具備相同的名字,Java中的方法重載發生在同一個類裏面兩個或者是多個方法的方法名相同可是參數不一樣的狀況。與此相對,方法覆蓋是說子類從新定義了父類的方法。方法覆蓋必須有相同的方法名,參數列表和返回類型。覆蓋者可能不會限制它所覆蓋的方法的訪問。

JSP與SERVLET的區別。

jsp頁面是在服務器端運行,其本質就是Servlet

JSP先編譯成servlet而後再編譯成class文件。JSP-----SERVLET-----JAVA文件---CLASS

jsp主要作視圖層,側重表現;servlet主要作控制層,側重邏輯

Servlet生命週期

當Tomcat第一次訪問Servlet的時候,首先加載servlet的class,實例化servlet,而後調用初始化方法 init() 初始化這個對象

當瀏覽器訪問Servlet的時候,Servlet 會調用service()方法處理請求

當Servlet服務器正常關閉時,執行destroy方法,只執行一次

servlet何時加載(執行init()方法)?

(1) load-on-startup 的值小於0 或者 不配置, 則在第一次請求的時候執行初始化。

(2) load-on-startup 的值大於等於0,則在tomcat啓動的時候就執行初始化。

ribbon和 feign 的區別?

一、Ribbon 和 feign 都是客戶端的負載均衡的工具,Feign的底層就是經過Ribbon實現的,它是對Ribbon的進一步的封裝,讓Ribbon 更加好用。
二、Ribbon 使用HttpClient 或 RestTemplate 模擬http請求,步驟至關繁瑣。而Feign採用接口+註解的方式 ,將須要調用的其餘服務的方法定義成抽象方法便可, 不須要本身構建http請求。而後就像是調用自身工程的方法調用,而感受不到是調用遠程方法,使得編寫 客戶端變得很是容易。相似於 mybatis 的 @Mapper註解 。

Spring經常使用組件

服務的註冊與發現(Eureka)
服務消費者(rest+ribbon)
服務消費者(Feign)
斷路器(Hystrix)
斷路器監控(Hystrix Dashboard)
斷路器聚合監控(Hystrix Turbine)
路由網關(zuul)
分佈式配置中心(Spring Cloud Config)
高可用的分佈式配置中心(Spring Cloud Config)
消息總線(Spring Cloud Bus)
服務鏈路追蹤(Spring Cloud Sleuth)
高可用的服務註冊中心
docker部署spring cloud項目
服務註冊(consul)

消息中間件

1.JMS即Java消息服務(Java Message Service)應用程序接口,是一個Java平臺中關於面向消息中間件(MOM)的API,用於在兩個應用程序之間,或分佈式系統中發送消息,進行異步通訊

2.ActiveMQ 是Apache出品,最流行的,能力強勁的開源消息總線,對JMS的支持較好

優勢:老牌的消息隊列,使用Java語言編寫。對JMS支持最好,採用多線程併發,資源消耗比較大。若是你的主語言是Java,能夠重點考慮。  

缺點:因爲歷史悠久,歷史包袱較多,版本更新很緩慢,幾個月才更新一次。集羣模式須要依賴Zookeeper實現。目前的研究重心在下一代產品Apollo上

3.RocketMQ/Kafka   

優勢:專爲海量消息傳遞打造,主張使用拉模式,自然的集羣、HA、負載均衡支持。話說仍是那句話,適合不適合看你有沒有那麼大的量。   

缺點:所謂魚和熊掌不可兼得,放棄了一些消息中間件的靈活性,使用的場景較窄,需關注你的業務模式是否契合

Kafka生態完善,其代碼是用Scala語言寫成,可靠性比RocketMQ低一些

4.RabbitMQ

優勢:生態豐富,使用者衆,有不少人在前面踩坑。AMQP協議的領導實現,支持多種場景。淘寶的MySQL集羣內部有使用它進行通信,OpenStack開源雲平臺的通訊組件,最早在金融行業獲得運用。

缺點:RabbitMQ使用Erlang編寫的,上手難度大,RabbitMQ在高可用方面作的通常

消息中間件的組成

Broker:消息服務器,做爲server提供消息核心服務

Producer:消息生產者,業務的發起方,負責生產消息傳輸給broker

Consumer:消息消費者,業務的處理方,負責從broker獲取消息並進行業務邏輯處理

Topic:主題,發佈訂閱模式下的消息統一聚集地,不一樣生產者向topic發送消息,由MQ服務器分發到不一樣的訂閱者,實現消息的廣播

Queue:隊列,PTP模式下,特定生產者向特定queue發送消息,消費者訂閱特定的queue完成指定消息的接收

Message:消息體,根據不一樣通訊協議定義的固定格式進行編碼的數據包,來封裝業務數據,實現消息的傳輸

消息中間件模式分類

1.點對點模式:使用queue做爲通訊載體,消息生產者生產消息發送到queue中,而後消息消費者從queue中取出而且消費消息。 消息被消費之後,queue中再也不存儲,因此消息消費者不可能消費到已經被消費的消息。 Queue支持存在多個消費者,可是對一個消息而言,只會有一個消費者能夠消費。

2.發佈/訂閱模式(廣播):使用topic做爲通訊載。消息生產者(發佈)將消息發佈到topic中,同時有多個消息消費者(訂閱)消費該消息。

3.路由模式 Routing:生產者發送消息到交換機並指定一個路由 key,消費者隊列綁定到交換機時要制定路由 key(key 匹配就能接受消息,key 不匹配就不能接受消息),例如:咱們能夠把路由 key 設置爲 insert ,那麼消費者隊列 key 指定包含 insert 才能夠接收消息,消費者隊列 key 定義爲 update 或者 delete 就不能接收消息。很好的控制了更新,插入和刪除的操做

4.通配符模式 Topics:此模式實在路由 key 模式的基礎上,使用了通配符來管理消費者接收消息

點對點和發佈訂閱的區別

queue實現了負載均衡,一條消息只能被一個消費者接收,當沒有消費者可用時,這個消息會被保存直到有一個可用的消費者,一個queue能夠有不少消費者,他們之間實現了負載均衡, 因此Queue實現了一個可靠的負載均衡。
topic實現了發佈和訂閱,當你發佈一個消息,全部訂閱這個topic的服務都能獲得這個消息,因此從1到N個訂閱者都能獲得一個消息的拷貝,發佈到topic的消息會被全部訂閱者消費

消息中間件的優點

1.解耦:傳統模式系統間耦合性太強;使用消息中間件後,交互系統之間沒有直接的調用關係,只是經過消息傳輸,故系統侵入性不強,耦合度低

2.異步:一些非必要的業務邏輯以同步的方式運行,太耗費時間;使用消息中間件後,將消息寫入消息隊列,非必要的業務邏輯以異步的方式運行,加快相應速度。例如原來的一套邏輯,完成支付可能涉及先修改訂單狀態、計算會員積分、通知物流配送幾個邏輯才能完成;經過MQ架構設計,就可將緊急重要(須要馬上響應)的業務放到該調用方法中,響應要求不高的使用消息隊列,放到MQ隊列中,供消費者處理

3.削峯:傳統模式併發量大的時候,全部的請求直接懟到數據庫,形成數據庫鏈接異常;使用MQ架構設計,系統慢慢的按照數據庫能處理的併發量,從消息隊列中拉取消息

消息中間件的缺點

系統可用性下降:在運行正常的系統加入消息隊列,那消息隊列掛了,系統也就不可用了,系統可用性會下降

系統複雜性增長:加入了消息隊列,要多考慮不少方面的問題,好比:一致性問題、如何保證消息不被重複消費、如何保證消息可靠性傳輸等。所以,須要考慮的東西更多,複雜性增大。

ActiveMQ服務器宕機怎麼辦

這得從ActiveMQ的儲存機制提及。在一般的狀況下,非持久化消息是存儲在內存中的,持久化消息是存儲在文件中的,它們的最大限制在配置文件的<systemUsage>節點中配置。可是,在非持久化消息堆積到必定程度,內存告急的時候,ActiveMQ會將內存中的非持久化消息寫入臨時文件中,以騰出內存。雖然都保存到了文件裏,但它和持久化消息的區別是,重啓後持久化消息會從文件中恢復,非持久化的臨時文件會直接刪除。解決方案就是儘可能不要用非持久化消息,非要用的話,將臨時文件限制儘量的調大

發送持久化消息很是慢

默認的狀況下,非持久化的消息是異步發送的,持久化的消息是同步發送的,遇到慢一點的硬盤,發送消息的速度是沒法忍受的。可是在開啓事務的狀況下,消息都是異步發送的,效率會有2個數量級的提高。因此在發送持久化消息時,請務必開啓事務模式。其實發送非持久化消息時也建議開啓事務,由於根本不會影響性能。

相關文章
相關標籤/搜索