java面試題集錦

2020-08-10 17:14:16

JAVA基礎

  1. JDK和JRE有什麼區別?

    簡單來說,JDK是面向開發人員使用的SDK開發環境,包含了軟件開發包,函數庫,編譯程式等等,JDK包含了JRE,JRE是Java執行環境,如果只需要執行Java程式,那麼安裝JRE就可以了。而JRE中包含了JVM,它是Java虛擬機器,當Java編譯器編譯Java程式時,生成的是與平臺無關的位元組碼檔案,這些位元組碼只面向JVM,不同環境的JVM都是不同的,但他們都提供了相同的介面。這就是Java語言的跨平臺原理:一次編譯,多處執行。

  2. ==和equals的區別是什麼?

    • ==操作符既可以比較基本數據型別,也可以用於比較物件,而equals只可以用於物件之間的比較。
    • 在比較String型別的物件時,==操作符的原理是隻有兩個變數是同一物件的參照纔會返回true,而equals方法只要兩個變數的內容相同就返回true,即 ==比較的是兩個物件的地址,而equals比較的是兩個物件的內容
    • 其他型別的變數進行比較時,equals方法預設的原理是判斷兩者的記憶體地址是否相同,所以預設情況下和操作符返回的結果相同,但是equals方法可以被重寫(使用者可以定製自己的equals方法),顯然操作符不能被重寫。
  3. 兩個物件的hashCode相同,則equals()也一定爲true,對嗎?

    不對!"通話"和"重地"兩個字串的hashCode一樣,但顯然equals比較爲false.

  4. final在java中有什麼作用?

    final作爲Java中的關鍵字可以用於三個地方,用於修飾類、類屬性和類方法。

    • 修飾類:表示該類不能被繼承
    • 修飾方法:表示方法不能被重寫
    • 修飾變數:變數只能一次賦值以後,該變數值不能被修改(常亮)
      • 當被final修飾的是一個基本數據型別時,這個數值在初始化後不能被修改
      • 當final修飾的是參照型別數據時,也就是修飾一個物件時,參照在初始化後將永遠指向一個記憶體地址,不可修改,但是該記憶體地址中儲存的物件資訊,是可以進行修改的。
  5. java中的Math.round(-1.5)等於多少?

    答案是-1;Math的round方法是四捨五入,如果參數是負數,則取大的整數,Math.round(-1.5)=-1,如果是Math.round(1.5)則結果爲2

  6. String屬於基礎的數據型別嗎?

    不屬於,String不是基本數據型別,而是一個類。

    Java八大基本數據型別:

    • 整形:byte、short、int、long
    • 浮點:float、double
    • 字元型:char
    • 布爾型別:boolean
  7. java中操作字串都有哪些類?他們之間有什麼區別?

    • String:final修飾,String類的方法都是返回new String,既String物件的任何改變都不影響到原物件,對字串的修改都會生成新的物件
    • StringBuffer:對字串的操作都加了synchronized,保證執行緒安全。
    • StringBuild:不保證執行緒安全,在方法體內需要進行字串的修改操作,可以new StringBuild物件,呼叫StringBuild物件的append()、replace()、delete()等方法修改字串。
  8. String str=「i」與String str=new String(「i」)一樣嗎?

    不一樣。

    • String str=「i」 java虛擬機器會將其分配到常數池中;在String str1="i"中,把i值存在常數池,地址賦給str1。假設再寫一個String str2=「i」,則會把i的地址賦給str2,但是i物件不會重新建立,他們參照的是同一個地址值,共用同一個i記憶體。常數池不會重複建立物件
    • 而String str=new String(「i」)會被分配到堆記憶體中,假設再寫一個String str3=new String(「i」),則會建立一個新的i物件,然後將新物件的地址值賦給str3。雖然str3和str1的值相同但是地址值不同。堆記憶體會建立新的物件
  9. 如何將字串反轉?

    • 使用StringBuild或StringBuffer的reverse方法
    • 不考慮字串中的字元是否是Unicode編碼,使用回圈和charAt自己實現。
    • 遞回方法
    • 。。。省略很多方法
  10. String類的常用方法都有哪些?

    • equals 比較字串是否相同
    • equalsIgnoreCase:忽略大小寫比較
    • indexOf 目標字元或字串在源字串中位置下標
    • lastIndexOf:目標字元或字串在源字串中最後一次出現的位置下標
    • valueOf:其他型別轉字串
    • isEmpty:字串長度是否爲0
    • concat:追加字串到當前字串
    • replace:字串替換
    • split:以某正則表達式分割字串
    • substring:擷取字串
    • toLowerCase:字串轉小寫
    • toUpperCase:字串轉大寫
    • 。。。。等等
  11. 抽象類必須要有抽象方法嗎?

    不必須:

    • 抽象類必須有關鍵字abstract來修飾
    • 抽象類可以不含有抽象方法
    • 如果一個類包含抽象方法,則該類必須是抽象類
  12. 普通類和抽象類有哪些區別?

    • 抽象類不能被範例化
    • 抽象類可以有抽象方法,抽象方法只需申明,無需實現
    • 含有抽象方法的類必須申明爲抽象類
    • 抽象類的子類必須實現抽象類的所有抽象方法,否則這個子類也是抽象類
    • 抽象方法不能申明爲靜態
    • 抽象方法不能用private修飾
    • 抽象方法不能用final修飾
  13. 抽象類能使用final修飾嗎?

    抽象類就是要子類繼承後來實現內部方法的,但final修飾的類是不能再被繼承和修改的,所以不能。

  14. 介面和抽象類有什麼區別?

    • 介面使用interface修飾,抽象方法使用abstract修飾
    • 抽象類內部可以有方法的實現細節,而介面只能存在public abstract方法
    • 介面中不能有靜態程式碼塊以及靜態方法,抽象類可以有
    • 一個類只能繼承一個抽象類,而一個類可以實現多個介面

    知識點:

    如果是Java 7,那麼介面中可以包含的內容有:
    1. 常數
    2. 抽象方法
    
    如果是Java 8,還可以額外包含有:
    3. 預設方法
    4. 靜態方法
    
    如果是Java 9,還可以額外包含有:
    5. 私有方法
    
    注意:介面中不能有變數和構造方法
    
  15. java中的IO流分爲幾種?

    • 按流向分類:輸入流、輸出流
    • 按讀寫數據分類:
      • 位元組流(讀寫任何型別的檔案)
        • 位元組輸出流:OutputStream(抽象類)
          –FileOutputStream :往檔案中寫位元組數據
        • 位元組輸入流(讀位元組)
          InputStream(抽象類)
          –FileInputStream : 讀取檔案中位元組數據
      • 字元流(專門用於讀寫文字檔案)
        • 字元輸出流(寫字元)
          Writer(抽象類)
          –FileWriter : 往檔案中寫字元數據
        • 字元輸入流(讀字元)
          Reader(抽象類)
          –FileReader : 讀取檔案中的字元數據
    • 按功能不同:
      • 節點流:包裹源頭
      • 處理流:增強功能,提高效能
  16. BIO、NIO、AIO有什麼區別?

    舉例講解:有一排水壺在燒開水

    • BIO 同步阻塞模式,一個執行緒停留在一個水壺,直到這個水壺燒開,採取處理下一個水壺,執行緒在等待水壺燒開的時間段什麼都沒有做
    • NIO 非同步非阻塞模式:同樣是一個執行緒,不斷輪詢每個水壺的狀態,卡看看是否有水壺狀態發生改變,再進行下一步操作
    • AIO 非同步非阻塞模式:無需一個執行緒去輪詢所有IO操作的狀態改變,在相應的狀態改變後,系統會通知對應的執行緒來處理,既爲每個水壺裝上了通知開關,水燒開之後,水壺自動通知我水已經燒開了。
  17. Files的常用方法有哪些?

    • File.exists() :檢查檔案路徑是否存在
    • Files.createFile():建立檔案
    • Files.copy() :複製檔案
    • Files.move() 移動檔案
    • Files.delete() 刪除檔案或者目錄
    • Files.read(): 讀取檔案
    • Files.write():寫入檔案

容器部分面試題

  1. java容器都有哪些?

    Collection介面下的List、Set、Queue和獨立的Map

  2. Collection和Collections有什麼區別

    • Collection是一個集合介面,提供了集合物件進行基本的操作的通用介面方法,爲各種具體的集合提供了最大化統一的操作方式
    • Collections是一個包裝類,它包含各種有關集合操作的靜態方法(集合的搜尋、排序、執行緒安全化等等),它不能被範例化,就像一個工具類,服務於JAVA的Collection
  3. List、Set、Map之間有什麼區別?

    • List:有序集合,元素可重複
    • Set:不可重複集合,LinkedHashSet按照插入排序,SortedSet可排序,HashSet無序
    • Map:鍵值對集合,儲存鍵值之間的對映,key無序,唯一,value不要求有序,可重複
  4. HaspMap和HashTable有什麼區別?

    • 父類別不同,HashMap繼承自AbstractMap,而HashTable繼承自Dictionary(已被廢棄)
    • null值問題:HashTable的key和value都不能爲空,HashMap中null可以作爲鍵,但只能有一個這樣的鍵,value可以有一個或者多個null。
    • 執行緒安全性:HashTable是執行緒安全的,它的每個方法都加入了Synchronized,但效率低。HashMap不是執行緒安全的,但它的效率會比HashTable高很多倍。
    • 初始容量和擴容:HashTable初始長度是11,之後每次擴容變爲之前的2n+1;HashMap初始長度是16,之後每次擴容變爲原來得兩倍。
    • 雜湊值得計算方法不同:HashTable直接使用物件的hashCode,hashCode是JDK根據物件的地址或者字串或者數位算出來的int型別的數值。然後再使用除留餘數發來獲得最終的位置。 然而除法運算是非常耗費時間的。效率很低。HashMap爲了提高計算效率,將雜湊表的大小固定爲了2的冪,這樣在取模預算時,不需要做除法,只需要做位運算。位運算比除法的效率要高很多。
    • 在單一執行緒下使用hashMap的速度要比hashTable要快,再多執行緒下可以使用hashTable來保證執行緒安全,但是現在多是選用currentHashMap,它的擴充套件性比hashTable要好。
  5. 如何決定使用HashMap還是TreeMap?

    • TreeMap<K,V>的Key值是要求實現java.lang.Comparable,所以迭代的時候TreeMap預設是按照Key值升序排序的;TreeMap的實現是基於紅黑樹結構。適用於按自然順序或自定義順序遍歷鍵(key)。
    • HashMap<K,V>的Key值實現雜湊hashCode(),分佈是雜湊的、均勻的,不支援排序;數據結構主要是桶(陣列),鏈表或紅黑樹。適用於在Map中插入、刪除和定位元素。
    • 總結:如果你需要得到一個有序的結果時就應該使用TreeMap(因爲HashMap中元素的排列順序是不固定的)。除此之外,由於HashMap有更好的效能,所以大多不需要排序的時候我們會使用HashMap。
  6. 說一說HashMap的實現原理

    1. 首先談談HashMap的數據結構,在java程式語言中,最基本的結構就是兩種:一個是陣列,一個是模擬指針(參照),hashMap實際上是一個數組和鏈表的結合體(在數據結構中,一般稱之爲「鏈表雜湊」)。
    2. hashMap基於Hashing原理,通過put()和get()方法來儲存和獲取物件,當我麼把鍵值對傳給put()方法時,它呼叫鍵物件的hashCode方法來獲取hashCode,然後找到bucket位置來儲存物件,當獲取物件時,通過鍵物件的equals()方法找到正確的鍵值對,然後返回值物件
    3. HashMap使用鏈表來解決碰撞問題,當發生碰撞了,物件會儲存在鏈表的下一個節點中,HashMap在每個鏈表節點中儲存鍵值對物件。
    4. 當兩個不同的鍵物件hashCode相同時會發生什麼?兩個物件就算hashcode相同,但是它們可能並不相等。他們會儲存在相同的bucket中,鍵物件的equals方法用來找到鍵值對。
  7. 說一說HashSet的實現原理

    1. HashSet底層使用了雜湊表來支援的,特點:儲存快
    2. 往HashSet新增元素的時候,HashSet會先呼叫元素的hashCode方法得到元素的雜湊值 ,然後通過元素的雜湊值經過移位等運算,就可以算出該元素在雜湊表中的儲存位置。
      • 如果算出的元素儲存的位置目前沒有任何元素儲存,那麼該元素可以直接儲存在該位置上
      • 如果算出的元素的儲存位置目前已經存在有其他的元素了,那麼還會呼叫該元素的equals方法
        與該位置的元素再比較一次,如果equals方法返回的是true,那麼該位置上的元素視爲重複元
        素,不允許新增,如果返回的是false,則允許新增、
  8. ArrayList和LinkedList的區別是什麼?

    • ArraryList(陣列結構):查詢快,增刪慢

    • LinkedList(鏈表結構):查詢慢,增刪快

      1. 執行緒安全:都是不同步的,都不保證執行緒安全

      2. 底層數據結構:ArrayList底層使用的是Object陣列,LinkedList底層使用雙向回圈鏈表數據結構

      3. 插入和刪除是否受元素位置影響:

        • ArrayList採用陣列結構,所以插入和刪除元素的時間複雜度受元素位置影響,例如在執行新增add(E e)時,ArrayList會預設將指定的元素追加到列表的末尾,時間複雜度爲0(1); 但是如果要在指定的位置i插入和刪除元素的話add(int index,E e),時間複雜度0(n-i)。因爲在進行上述操作的時候,集合第i和第i個之後的(n-i)個元素都要執行向前/後移一位的操作。
        • LinkedList採用鏈表儲存,所以插入和刪除不受元素位置影響,都是近似0(1),而陣列近似爲0(n)
      4. 是否支援快速隨機存取:LinkedList不支援,ArrayList實現了RandmoAccess介面,所以有隨機存取的功能,快速存取就是通過元素的序號快速獲取元素,對應get(int index)方法

      5. 記憶體佔用空間:ArrayList記憶體空間浪費主要體現在list列表的結尾都會預留一定的空間容量,而LinkedList的空間花費體現在每一個元素都需要消耗比ArrayList更多的空間(因爲要存放直接後繼和間接前驅以及數據)

  9. 如何實現陣列和List之間的轉換?

    • 陣列轉 List:
      • 使用 JDK 中 java.util.Arrays 工具類的 asList 方法
      • 使用Stream中的Collector收集器:Stream.of(str).collect(Collectors.toList());
    • List轉陣列:使用 List 的toArray方法。無參toArray方法返回Object陣列,傳入初始化長度的陣列物件,返回該物件陣列
    • 回圈轉換
  10. ArrayList和Vector的區別是什麼?

    • Vector底層和ArrayList集合相同,但Vector是執行緒安全的,效率較低,一般很少使用。
    • 擴容:Vector會將容量翻倍,但ArrayList只增加50%,ArrayList有利於節省空間。
  11. Array和ArrayList有何區別?

    • 定義一個 Array 時,必須指定陣列的數據型別及陣列長度,即陣列中存放的元素個數固定並且型別相同。
    • ArrayList 是動態陣列,長度動態可變,會自動擴容。不使用泛型的時候,可以新增不同類型元素。
  12. 在Queue中poll()和remove()有什麼區別?

    當佇列爲空時,remove會拋異常,poll返回null;

  13. 哪些集合類是執行緒安全的?

    • Vector:就比Arraylist多了個同步化機制 機製(執行緒安全)。

    • Hashtable:就比Hashmap多了個執行緒安全。

    • ConcurrentHashMap:是一種高效但是執行緒安全的集合。

    • Stack:棧,也是執行緒安全的,繼承於Vector。

  14. 迭代器iterator是什麼?

    • 首先說一下迭代器模式,它是 Java 中常用的設計模式之一。用於順序存取集合物件的元素,無需知道集合物件的底層實現。
    • Iterator 是可以遍歷集合的物件,爲各種容器提供了公共的操作介面,隔離對容器的遍歷操作和底層實現,從而解耦。
    • 缺點是增加新的集合類需要對應增加新的迭代器類,迭代器類與集合類成對增加。
  15. Iterator怎麼使用?有什麼特點?

    • java.lang.Iterable 介面被 java.util.Collection 介面繼承,java.util.Collection 介面的 iterator() 方法返回一個 Iterator 物件
    • next() 方法獲得集閤中的下一個元素
    • hasNext() 檢查集閤中是否還有元素
    • remove() 方法將迭代器新返回的元素刪除
    • forEachRemaining(Consumer<? super E> action) 方法,遍歷所有元素
  16. Iterator和ListIterator有什麼區別?

    • ListIterator繼承Iterator
    • 使用範圍不同,Iterator可以應用於所有的集合,Set、List和Map和這些集合的子型別。而ListIterator只能用於List及其子型別。
    • ListIterator有add方法,可以向List中新增物件,而Iterator不能。
    • ListIterator和Iterator都有hasNext()和next()方法,可以實現順序向後遍歷,但是ListIterator有hasPrevious()和previous()方法,可以實現逆向(順序向前)遍歷。Iterator不可以。
    • ListIterator可以定位當前索引的位置,nextIndex()和previousIndex()可以實現。Iterator沒有此功能。
    • 都可實現刪除操作,但是ListIterator可以實現物件的修改,set()方法可以實現。Iterator僅能遍歷,不能修改。
  17. 怎麼確保一個集合不能被修改?

    我們可以採用Collections包下的unmodifiableMap方法,通過這個方法返回的map,是不可以修改的。他會報 java.lang.UnsupportedOperationException錯。

    同理:Collections包也提供了對list和set集合的方法。
    Collections.unmodifiableList(List)
    Collections.unmodifiableSet(Set)。。。。。。

多執行緒

  1. 並行和併發有什麼區別?序列又是什麼?

    • 序列:序列指的是一個所有的任務都按照先後順序執行,在前一個任務沒處理完的情況下是不會去處理下一個任務的,就像理髮店只有一個理髮師,每個人剪頭髮都需要等待前面的人處理完
    • 並行:並行是將任務分配給不同的處理器去處理,每一個處理器再序列處理,比如火車站多售票口
    • 併發:併發實質上是一種現象,併發的需要處理器的支援,比如在處理一個任務的時候操作系統可以進行排程再處理其他任務,不論序列還是並行,都需要操作系統支援併發,假設喝水是一個任務,那麼每個火車售票員在賣票的同時也能喝水,那麼就支援併發
  2. 執行緒和進程的區別?

    • 進程:是執行中一段程式,即一旦程式被載入到記憶體中並準備執行,它就是一個進程。進程是表示資源分配的的基本概念,又是排程執行的基本單位,是系統中的併發執行的單位。
    • 執行緒:單個進程中執行中每個任務就是一個執行緒。執行緒是進程中執行運算的最小單位。一個執行緒只能屬於一個進程。
  3. 守護執行緒是什麼?

    守護執行緒擁有自動結束自己生命週期的特性,而非守護執行緒不具備這個特點。JVM 中的垃圾回收執行緒就是典型的守護執行緒。守護執行緒經常被用來執行一些後臺任務,但是呢,你又希望在程式退出時,或者說 JVM 退出時,執行緒能夠自動關閉,此時,守護執行緒是你的首選。

    • setDaemon(true) 必須在 start() 之前設定,否則會拋出IllegalThreadStateException異常,該執行緒仍預設爲使用者執行緒,繼續執行
    • 守護執行緒建立的執行緒也是守護執行緒
    • 守護執行緒不應該存取、寫入持久化資源,如檔案、數據庫,因爲它會在任何時間被停止,導致資源未釋放、數據寫入中斷等問題
  4. 建立執行緒有哪幾種方式?

    • 繼承Thread類建立執行緒類

    • 通過Runnable介面建立執行緒類

    • 實現Callable介面

      @Test
          public void test() throws ExecutionException, InterruptedException {
              CallableDemoTest callableDemoTest = new CallableDemoTest();
              FutureTask futureTask = new FutureTask(callableDemoTest);
              Thread thread = new Thread(futureTask);
              thread.start();
              //獲取返回值
              futureTask.get();
          }
      
    • 執行緒池

  5. 說一下runnable()和callable有什麼區別?

    • 兩者最大的不同點是:實現Callable介面的任務執行緒能返回執行結果;而實現Runnable介面的任務執行緒不能返回結果;
    • Callable介面的call()方法允許拋出異常;而Runnable介面的run()方法的異常只能在內部消化,不能繼續上拋;
    • 注意:Callable介面支援返回執行結果,此時需要呼叫FutureTask.get()方法實現,此方法會阻塞主執行緒直到獲取‘將來’結果;當不呼叫此方法時,主執行緒不會阻塞!
  6. 執行緒有哪些狀態?

    1. 初始(NEW):新建立了一個執行緒物件,但還沒有呼叫start()方法。
    2. 執行(RUNNABLE):Java執行緒中將就緒(ready)和執行中(running)兩種狀態籠統的稱爲「執行」。執行緒物件建立後,其他執行緒(比如main執行緒)呼叫了該物件的start()方法。該狀態的執行緒位於可執行執行緒池中,等待被執行緒排程選中,獲取CPU的使用權,此時處於就緒狀態(ready)。就緒狀態的執行緒在獲得CPU時間片後變爲執行中狀態(running)。
    3. 阻塞(BLOCKED):表示執行緒阻塞於鎖。
    4. 等待(WAITING):進入該狀態的執行緒需要等待其他執行緒做出一些特定動作(通知或中斷)。
    5. 超時等待(TIMED_WAITING):該狀態不同於WAITING,它可以在指定的時間後自行返回。
    6. 終止(TERMINATED):表示該執行緒已經執行完畢。
  7. sleep()和wait有什麼區別?

    sleep() 方法是執行緒類(Thread)的靜態方法,使呼叫執行緒進入睡眠狀態,休眠結束後執行緒進入就緒狀態 wait()是Object類的方法,當執行緒執行到wait方法,進入到和該物件相關的等待池,同時釋放物件的機鎖(使得其他執行緒能夠存取),通過notify,notifyAll方法喚醒執行緒。

    sleep() 和 wait() 的區別就是 呼叫sleep方法的執行緒不會釋放物件鎖,而呼叫wait() 方法會釋放物件鎖。因爲一個類中的靜態資源在類class被java虛擬機器載入時就已經被初始化了,而非靜態資源要在物件範例化的時候纔會被初始化。sleep()方法是靜態的,只依賴於類,不依賴於物件,而鎖是物件鎖,所以不能改變鎖

  8. notify()和notifyAll()有什麼區別?

    1.先說兩個概念:鎖池,等待池

    • 鎖池:假設執行緒A已經擁有了某個物件(注意:不是類)的鎖,而其它的執行緒想要呼叫這個物件的某個synchronized方法(或者synchronized塊),由於這些執行緒在進入物件的synchronized方法之前必須先獲得該物件的鎖的擁有權,但是該物件的鎖目前正被執行緒A擁有,所以這些執行緒就進入了該物件的鎖池中。
    • 等待池:假設一個執行緒A呼叫了某個物件的wait()方法,執行緒A就會釋放該物件的鎖後,進入到了該物件的等待池。

    2.然後再來說notify和notifyAll的區別

    • 如果執行緒呼叫了物件的 wait()方法,那麼執行緒便會處於該物件的等待池中,等待池中的執行緒不會去競爭該物件的鎖
    • 當有執行緒呼叫了物件的 notifyAll()方法(喚醒所有 wait 執行緒)或 notify()方法(只隨機喚醒一個 wait 執行緒),被喚醒的的執行緒便會進入該物件的鎖池中,鎖池中的執行緒會去競爭該物件鎖。也就是說,呼叫了notify後只要一個執行緒會由等待池進入鎖池,而notifyAll會將該物件等待池內的所有執行緒移動到鎖池中,等待鎖競爭
    • 優先順序高的執行緒競爭到物件鎖的概率大,假若某執行緒沒有競爭到該物件鎖,它還會留在鎖池中,唯有執行緒再次呼叫 wait()方法,它纔會重新回到等待池中。而競爭到物件鎖的執行緒則繼續往下執行,直到執行完了 synchronized 程式碼塊,它會釋放掉該物件鎖,這時鎖池中的執行緒會繼續競爭該物件鎖。
  9. 執行緒的run()和start()有什麼區別?

    • run()相當於執行緒的任務處理邏輯的入口方法,它由Java虛擬機器在執行相應執行緒時直接呼叫,而不是由應用程式碼進行呼叫。
    • start()的作用是啓動相應的執行緒。啓動一個執行緒實際是請求Java虛擬機器執行相應的執行緒,而這個執行緒何時能夠執行是由執行緒排程器決定的。start()呼叫結束並不表示相應執行緒已經開始執行,這個執行緒可能稍後執行,也可能永遠也不會執行
  10. 建立執行緒池有哪幾種方式?

    • 通過其提供的介面Executors來建立執行緒池,通過newCachedThreadPool來建立一個可以快取的執行緒池,理論上通過該方法可以建立無限個執行緒
    • 通過newFixedThreadPool來建立一個定長執行緒池,假設我們固定定長爲1,然後建立3個執行緒,那麼他就會等第一個執行緒執行完了之後纔會建立第2個執行緒,所以可以發現,三個執行緒的名字是一樣的,因爲每個執行緒建立之後上一個執行緒已經執行完了
    • 通過newScheduledThreadPool建立一個延時執行緒池,每一個執行緒之間都延遲了定義的秒數
    • 通過newSingleThreadExecutor來建立一個執行緒池,這個和其他執行緒池的最大區別是不需要設定定長的數量,它只允許同時執行一個執行緒
  11. 執行緒池都有哪些狀態?

    執行緒池的5種狀態:

    • RUNNING:執行緒池一旦被建立,就處於 RUNNING 狀態,任務數爲 0,能夠接收新任務,對已排隊的任務進行處理。
    • SHUTDOWN:不接收新任務,但能處理已排隊的任務。呼叫執行緒池的 shutdown() 方法,執行緒池由 RUNNING 轉變爲 SHUTDOWN 狀態。
    • STOP:不接收新任務,不處理已排隊的任務,並且會中斷正在處理的任務。呼叫執行緒池的 shutdownNow() 方法,執行緒池由(RUNNING 或 SHUTDOWN ) 轉變爲 STOP 狀態。
    • TIDYING:
      • SHUTDOWN 狀態下,任務數爲 0, 其他所有任務已終止,執行緒池會變爲 TIDYING 狀態,會執行 terminated() 方法。執行緒池中的 terminated() 方法是空實現,可以重寫該方法進行相應的處理。
      • 執行緒池在 SHUTDOWN 狀態,任務佇列爲空且執行中任務爲空,執行緒池就會由 SHUTDOWN 轉變爲 TIDYING 狀態。
      • 執行緒池在 STOP 狀態,執行緒池中執行中任務爲空時,就會由 STOP 轉變爲 TIDYING 狀態。
    • TERMINATED:執行緒池徹底終止。執行緒池在 TIDYING 狀態執行完 terminated() 方法就會由 TIDYING 轉變爲 TERMINATED 狀態。
  12. 執行緒池中submit()和execute方法有什麼區別?

    兩個方法都可以向執行緒池提交任務

    • execute()方法的返回型別是 void,它定義在Executor 介面中。
    • 而 submit()方法可以返回持有計算結果的 Future 物件,它定義在ExecutorService 介面中,它擴充套件了 Executor 介面,其它執行緒池類像ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 都有這些方法。
  13. 在Java程式中怎麼保證多執行緒的執行安全?

    執行緒的安全性問題體現在:

    • 原子性:一個或者多個操作在 CPU 執行的過程中不被中斷的特性
    • 可見性:一個執行緒對共用變數的修改,另外一個執行緒能夠立刻看到
    • 有序性:程式執行的順序按照程式碼的先後順序執行

    導致原因:

    • 快取導致的可見性問題
    • 執行緒切換帶來的原子性問題
    • 編譯優化帶來的有序性問題

    解決方法:

    1. 使用synchronied關鍵字,可以用於程式碼塊,方法(靜態方法,同步鎖是當前位元組碼物件;實體方法,同步鎖是範例物件)
    2. 使用volatile 關鍵字,防止指令重排,被volatile修飾的變數的值,將不會被本地執行緒快取,所有對該變數的讀寫都是直接操作共用記憶體,從而確保多個執行緒能正確的處理該變數
    3. lock鎖機制 機製
  14. synchronized和volatile的區別是什麼?

    作用:

    • synchronized 表示只有一個執行緒可以獲取作用物件的鎖,執行程式碼,阻塞其他執行緒。
    • volatile 表示變數在 CPU 的暫存器中是不確定的,必須從主記憶體中讀取。保證多執行緒環境下變數的可見性;禁止指令重排序。

    區別:

    • synchronized 可以作用於變數、方法、物件;volatile 只能作用於變數。
    • synchronized 可以保證執行緒間的有序性(猜測是無法保證執行緒內的有序性,即執行緒內的程式碼可能被 CPU 指令重排序)、原子性和可見性;volatile 只保證了可見性和有序性,無法保證原子性。
    • synchronized 執行緒阻塞,volatile 執行緒不阻塞。
  15. synchronized和Lock有什麼區別?

    兩者區別:

    1.首先synchronized是java內建關鍵字,在jvm層面,Lock是個java類;

    2.synchronized無法判斷是否獲取鎖的狀態,Lock可以判斷是否獲取到鎖;

    3.synchronized會自動釋放鎖(a 執行緒執行完同步程式碼會釋放鎖 ;b 執行緒執行過程中發生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),否則容易造成執行緒死鎖;

    4.用synchronized關鍵字的兩個執行緒1和執行緒2,如果當前執行緒1獲得鎖,執行緒2執行緒等待。如果執行緒1阻塞,執行緒2則會一直等待下去,而Lock鎖就不一定會等待下去,如果嘗試獲取不到鎖,執行緒可以不用一直等待就結束了;

    5.synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(兩者皆可)

    6.Lock鎖適合大量同步的程式碼的同步問題,synchronized鎖適合程式碼少量的同步問題。

Spring、SpringMVC部分

  1. 爲什麼要使用spring?

    spring 是一個開源的輕量級 JavaBean 容器框架。使用 JavaBean 代替 EJB ,並提供了豐富的企業應用功能,降低應用開發的複雜性。

    • 輕量:非入侵性的、所依賴的東西少、資源佔用少、部署簡單,不同功能選擇不同的 jar 組合
    • 容器:工廠模式實現對 JavaBean 進行管理,通過控制反轉(IOC)將應用程式的設定和依賴性與應用程式碼分開
    • 松耦合:通過 xml 設定或註解即可完成 bean 的依賴注入
    • AOP:通過 xml 設定 或註解即可加入面向切面程式設計的能力,完成切面功能,如:日誌,事務…的統一處理
    • 方便整合:通過設定和簡單的物件注入即可整合其他框架,如 Mybatis、Hibernate、Shiro…
    • 豐富的功能:JDBC 層抽象、事務管理、MVC、Java Mail、任務排程、JMX、JMS、JNDI、EJB、動態語言、遠端存取、Web Service…
  2. 解釋一下什麼是AOP?

    使用AOP技術,可以將一些系統性相關的程式設計工作,獨立提取出來,獨立實現,然後通過切面切入進系統。從而避免了在業務邏輯的程式碼中混入很多的系統相關的邏輯——比如許可權管理,事物管理,日誌記錄等等。這些系統性的程式設計工作都可以獨立編碼實現,然後通過AOP技術切入進系統即可。從而達到了 將不同的關注點分離出來的效果。

  3. 解釋一下什麼是IOC?

    IoC就是Inversion of Control,控制反轉。在Java開發中,IoC意味着將你設計好的類交給系統去控制,而不是在你的類內部控制。這稱爲控制反轉。就是把原本你自己製造,使用的物件,現在交由別人製造,而通過建構函式,setter方法或方法(這裏指使用這個物件的方法)參數的方式傳給你,由你使用。

  4. Spring有哪些主要模組?

    1. Spring Core
      框架的最基礎部分,提供 IoC 容器,對 bean 進行管理。
    2. Spring Context
      基於 bean,提供上下文資訊,擴充套件出JNDI、EJB、電子郵件、國際化、校驗和排程等功能。
    3. Spring DAO
      提供了JDBC的抽象層,它可消除冗長的JDBC編碼和解析數據庫廠商特有的錯誤程式碼,還提供了宣告性事務管理方法。
    4. Spring ORM
      提供了常用的「物件/關係」對映APIs的整合層。 其中包括JPA、JDO、Hibernate、MyBatis 等。
    5. Spring AOP
      提供了符合AOP Alliance規範的面向方面的程式設計實現。
    6. Spring Web
      提供了基礎的 Web 開發的上下文資訊,可與其他 web 進行整合。
    7. Spring Web MVC
      提供了 Web 應用的 Model-View-Controller 全功能實現。
  5. Spring常用的注入方式有哪些?

    常用的注入方式主要有三種:構造方法注入,setter注入,基於註解的注入。

  6. Spring中的Bean是執行緒安全的嗎?

    Spring容器中的Bean是否執行緒安全,容器本身並沒有提供Bean的執行緒安全策略,因此可以說Spring容器中的Bean本身不具備執行緒安全的特性,但是具體還是要結合具體scope的Bean去研究。

    • 對於原型Bean,每次建立一個新物件,也就是執行緒之間並不存在Bean共用,自然是不會有執行緒安全的問題。

    • 對於單例Bean,所有執行緒都共用一個單例範例Bean,因此是存在資源的競爭。

      如果單例Bean,是一個無狀態Bean,也就是執行緒中的操作不會對Bean的成員執行查詢以外的操作,那麼這個單例Bean是執行緒安全的。比如Spring mvc 的 Controller、Service、Dao等,這些Bean大多是無狀態的,只關注於方法本身。

      對於有狀態的bean,Spring官方提供的bean,一般提供了通過ThreadLocal去解決執行緒安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等。
      注: Spring容器本身並沒有提供執行緒安全的策略,因此是否執行緒安全完全取決於Bean本身的特性。

  7. Spring支援幾種bean的作用域?

    1、singleton:單例,預設作用域。

    2、prototype:原型,每次建立一個新物件。

    3、request:請求,每次Http請求建立一個新物件,適用於WebApplicationContext環境下。

    4、session:對談,同一個對談共用一個範例,不同對談使用不用的範例。

    5、global-session:全域性對談,所有對談共用一個範例。

  8. Spring自動裝配bean有哪些方式?

    • default - 預設的方式和 「no」 方式一樣
    • no - 不自動裝配,需要使用 節點或參數
    • byName - 根據名稱進行裝配
    • byType - 根據型別進行裝配
    • constructor - 根據建構函式進行裝配
  9. Spring事務實現方式有哪些?

    1. 通過事務代理工廠bean進行設定(xml方式)
    2. 註解:@Transactional
    3. Aspectj AOP設定事務
  10. 說一下Spring的事務隔離?

    1. DEFAULT 預設設定:使用數據庫的隔離設定
    2. READ_UNCOMMITTED (讀未提交數據):允許事務讀取未被其他事務提交的變更數據,會出現髒讀、不可重複讀和幻讀問題。
    3. READ_COMMITTED (讀已提交數據):只允許事務讀取已經被其他事務提交的變更數據,可避免髒讀,仍會出現不可重複讀和幻讀問題。
    4. REPEATABLE_READ (可重複讀):確保事務可以多次從一個欄位中讀取相同的值,在此事務持續期間,禁止其他事務對此欄位的更新,可以避免髒讀和不可重複讀,仍會出現幻讀問題。
    5. SERIALIZABLE (序列化):確保事務可以從一個表中讀取相同的行,在這個事務持續期間,禁止其他事務對該表執行插入、更新和刪除操作,可避免所有併發問題,但效能非常低。
  11. 說一下SpringMVC執行流程?

    1. 前端頁面發送Request請求給前端控制器:DispatcherServlet
    2. DispatcherServlet 請求查詢Handler 到處理器對映器HandlerMapping
    3. HandlerMapping 返回一個執行鏈:HandlerExecutionChain{ HandlerInterceptor1 HandlerInterceptor2 HandlerInterceptor3 … Handler } 給前端控制器DispatcherServlet
    4. 前端控制器請求適配器HandlerAdapter執行
    5. 處理器Controller執行邏輯,呼叫業務層(Service層),業務層呼叫持久層(DAO)層,然後逐層返回到Controller
    6. Controller 返回 ModelAndView 給適配器
    7. 適配器返回ModelAndView 給 DispatcherServlet
    8. DispatcherServlet請求檢視解析器(ViewResolver)進行檢視解析,
    9. 檢視解析器返回View給DispatcherServlet
    10. 進行檢視渲染,將模型數據填充到request域
    11. DispatcherServlet給前臺頁面response響應
  12. SpringMVC有哪些元件?

    1. 前端控制器元件(DispatcherServlet)
    2. 處理器元件(Controller)
    3. 處理器對映器元件(HandlerMapping)
    4. 處理器適配器元件(HandlerAdapter)
    5. 攔截器元件(HandlerInterceptor)
    6. 檢視解析器元件(ViewResolver)
    7. 檢視元件(View)
    8. 數據轉換元件(DataBinder)
    9. 訊息轉換器元件(HttpMessageConverter)
  13. @RequestMapping的作用是什麼?

    是一個用來處理請求地址對映的註解,可用於類或者方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作爲父路徑。

  14. @Autowired的作用是什麼?

    @Autowired 是一個註釋,它可以對類成員變數、方法及建構函式進行標註,讓 spring 完成 bean 自動裝配的工作。

    @Autowired 預設是按照類去匹配,配合 @Qualifier 指定按照名稱去裝配 bean。

Mybatis面試題

  1. mybatis中#{}和${}的區別是什麼?

    1. #將傳入的數據都當成一個字串,會對自動傳入的數據加一個雙引號。如:order by #user_id#,如果傳入的值是111,那麼解析成sql時的值爲order by 「111」, 如果傳入的值是id,則解析成的sql爲order by 「id」.
    2. #{} : 根據參數的型別進行處理,比如傳入String型別,則會爲參數加上雙引號。#{} 傳參在進行SQL預編譯時,會把參數部分用一個佔位符 ? 代替,這樣可以防止 SQL隱碼攻擊。
    3. ${} : 將參數取出不做任何處理,直接放入語句中,就是簡單的字串替換,並且該參數會參加SQL的預編譯,需要手動過濾參數防止 SQL隱碼攻擊。
    4. 因此 mybatis 中優先使用 #{};當需要動態傳入 表名或列名時,再考慮使用 ${} , 比較特殊,他的應用場景是需要動態傳入表名或列名時使用,MyBatis排序時使用orderby動態參數時需要注意,用{} 比較特殊, 他的應用場景是 需要動態傳入 表名或列名時使用,MyBatis排序時使用order by 動態參數時需要注意,用比較特殊,他的應用場景是需要動態傳入表名或列名時使用,MyBatis排序時使用orderby動態參數時需要注意,用而不是#。
  2. mybatis有幾種分頁模式?

    1. 陣列分頁:進行數據庫查詢操作時,獲取到數據庫中所有滿足條件的記錄,儲存在應用的臨時陣列中,再通過List的subList方法,獲取到滿足條件的所有記錄。

    2. 藉助sql語句進行分頁:通過sql語句實現分頁也是非常簡單的,只是需要改變我們查詢的語句就能實現了,即在sql語句後面新增limit分頁語句。

    3. RowBounds實現分頁:通過RowBounds實現分頁和通過陣列方式分頁原理差不多,都是一次獲取所有符合條件的數據,然後在記憶體中對大數據進行操作,實現分頁效果。只是陣列分頁需要我們自己去實現分頁邏輯,這裏更加簡化而已。

      存在問題:一次性從數據庫獲取的數據可能會很多,對記憶體的消耗很大,可能導師效能變差,甚至引發記憶體溢位。

      適用場景:在數據量很大的情況下,建議還是適用攔截器實現分頁效果。RowBounds建議在數據量相對較小的情況下使用。

    4. 攔截器分頁:自定義攔截器實現了攔截所有以ByPage結尾的查詢語句,並且利用獲取到的分頁相關參數統一在sql語句後面加上limit分頁的相關語句,一勞永逸。不再需要在每個語句中單獨去設定分頁相關的參數了。。

  3. RowBounds是一次性查詢全部結果嗎?爲什麼?

    RowBounds 表面是在「所有」數據中檢索數據,其實並非是一次性查詢出所有數據,因爲 MyBatis 是對 jdbc 的封裝,在 jdbc 驅動中有一個 Fetch Size 的設定,它規定了每次最多從數據庫查詢多少條數據,假如你要查詢更多數據,它會在你執行 next()的時候,去查詢更多的數據。就好比你去自動取款機取 10000 元,但取款機每次最多能取 2500 元,所以你要取 4 次才能 纔能把錢取完。只是對於 jdbc 來說,當你呼叫 next()的時候會自動幫你完成查詢工作。這樣做的好處可以有效的防止記憶體溢位。

  4. mybatis邏輯分頁和物理分頁的區別是什麼?

    邏輯分頁是一次性查詢很多數據,然後再在結果中檢索分頁的數據。這樣做弊端是需要消耗大量的記憶體、有記憶體溢位的風險、對數據庫壓力較大。
    物理分頁是從數據庫查詢指定條數的數據,彌補了一次性全部查出的所有數據的種種缺點,比如需要大量的記憶體,對數據庫查詢壓力較大等問題。

  5. mybatis是否支援延遲載入?延遲載入的原理是什麼?

    MyBatis 支援延遲載入,設定 lazyLoadingEnabled=true 即可。
    延遲載入的原理的是呼叫的時候觸發載入,而不是在初始化的時候就載入資訊。比如呼叫 a. getB(). getName(),這個時候發現 a. getB() 的值爲 null,此時會單獨觸發事先儲存好的關聯 B 物件的 SQL,先查詢出來 B,然後再呼叫 a. setB(b),而這時候再呼叫 a. getB(). getName() 就有值了,這就是延遲載入的基本原理。

  6. 說一下mybatis的一級快取和二級快取?

    一級快取:基於 PerpetualCache 的 HashMap 本地快取,它的宣告週期是和 SQLSession 一致的,有多個 SQLSession 或者分佈式的環境中數據庫操作,可能會出現髒數據。當 Session flush 或 close 之後,該 Session 中的所有 Cache 就將清空,預設一級快取是開啓的。
    二級快取:也是基於 PerpetualCache 的 HashMap 本地快取,不同在於其儲存作用域爲 Mapper 級別的,如果多個SQLSession之間需要共用快取,則需要使用到二級快取,並且二級快取可自定義儲存源,如 Ehcache。預設不開啓二級快取,要開啓二級快取,使用二級快取屬性類需要實現 Serializable 序列化介面(可用來儲存物件的狀態)。
    開啓二級快取數據查詢流程:二級快取 -> 一級快取 -> 數據庫。
    快取更新機制 機製:當某一個作用域(一級快取 Session/二級快取 Mapper)進行了C/U/D 操作後,預設該作用域下所有 select 中的快取將被 clear。

  7. mybatis和hibernate的區別有哪些?

    靈活性:MyBatis 更加靈活,自己可以寫 SQL 語句,使用起來比較方便。
    可移植性:MyBatis 有很多自己寫的 SQL,因爲每個數據庫的 SQL 可以不相同,所以可移植性比較差。
    學習和使用門檻:MyBatis 入門比較簡單,使用門檻也更低。
    二級快取:hibernate 擁有更好的二級快取,它的二級快取可以自行更換爲第三方的二級快取。

  8. mybatis有哪些執行器(Executor)?

    MyBatis 有三種基本的Executor執行器:
    SimpleExecutor:每執行一次 update 或 select 就開啓一個 Statement 物件,用完立刻關閉 Statement 物件;
    ReuseExecutor:執行 update 或 select,以 SQL 作爲 key 查詢 Statement 物件,存在就使用,不存在就建立,用完後不關閉 Statement 物件,而是放置於 Map 內供下一次使用。簡言之,就是重複使用 Statement 物件;
    BatchExecutor:執行 update(沒有 select,jdbc 批次處理不支援 select),將所有 SQL 都新增到批次處理中(addBatch()),等待統一執行(executeBatch()),它快取了多個 Statement 物件,每個 Statement 物件都是 addBatch()完畢後,等待逐一執行 executeBatch()批次處理,與 jdbc 批次處理相同。

  9. mybatis分頁外掛的實現原理是什麼?

    分頁外掛的基本原理是使用 MyBatis 提供的外掛介面,實現自定義外掛,在外掛的攔截方法內攔截待執行的 SQL,然後重寫 SQL,根據 dialect 方言,新增對應的物理分頁語句和物理分頁參數。

  10. mybatis如何編寫一個自定義外掛?

    自定義外掛實現原理

    MyBatis 自定義外掛針對 MyBatis 四大物件(Executor、StatementHandler、ParameterHandler、ResultSetHandler)進行攔截:

    Executor:攔截內部執行器,它負責呼叫 StatementHandler 操作數據庫,並把結果集通過 ResultSetHandler 進行自動對映,另外它還處理了二級快取的操作;
    StatementHandler:攔截 SQL 語法構建的處理,它是 MyBatis 直接和數據庫執行 SQL 指令碼的物件,另外它也實現了 MyBatis 的一級快取;
    ParameterHandler:攔截參數的處理;
    ResultSetHandler:攔截結果集的處理。
    自定義外掛實現關鍵

JVM部分面試題

  1. 說一下JVM的主要組成部分及其作用

    類載入器(ClassLoader)
    執行時數據區(Runtime Data Area)
    執行引擎(Execution Engine)
    本地庫介面(Native Interface)
    元件的作用: 首先通過類載入器(ClassLoader)會把 Java 程式碼轉換成位元組碼,執行時數據區(Runtime Data Area)再把位元組碼載入到記憶體中,而位元組碼檔案只是 JVM 的一套指令集規範,並不能直接交給底層操作系統去執行,因此需要特定的命令解析器執行引擎(Execution Engine),將位元組碼翻譯成底層系統指令,再交由 CPU 去執行,而這個過程中需要呼叫其他語言的本地庫介面(Native Interface)來實現整個程式的功能。

  2. 說一下JVM執行時數據區

    不同虛擬機器的執行時數據區可能略微有所不同,但都會遵從 Java 虛擬機器規範, Java 虛擬機器規範規定的區域分爲以下 5 個部分:
    程式計數器(Program Counter Register):當前執行緒所執行的位元組碼的行號指示器,位元組碼解析器的工作是通過改變這個計數器的值,來選取下一條需要執行的位元組碼指令,分支、回圈、跳轉、例外處理、執行緒恢復等基礎功能,都需要依賴這個計數器來完成;
    Java 虛擬機器棧(Java Virtual Machine Stacks):用於儲存區域性變數表、運算元棧、動態鏈接、方法出口等資訊;
    本地方法棧(Native Method Stack):與虛擬機器棧的作用是一樣的,只不過虛擬機器棧是服務 Java 方法的,而本地方法棧是爲虛擬機器呼叫 Native 方法服務的;
    Java 堆(Java Heap):Java 虛擬機器中記憶體最大的一塊,是被所有執行緒共用的,幾乎所有的物件範例都在這裏分配記憶體;
    方法區(Methed Area):用於儲存已被虛擬機器載入的類資訊、常數、靜態變數、即時編譯後的程式碼等數據。

  3. 說一下堆疊區別

    功能方面:堆是用來存放物件的,棧是用來執行程式的。
    共用性:堆是執行緒共用的,棧是執行緒私有的。
    空間大小:堆大小遠遠大於棧。

  4. 佇列和棧是什麼,有什麼區別?

    佇列和棧都是被用來預儲存數據的。
    佇列允許先進先出檢索元素,但也有例外的情況,Deque 介面允許從兩端檢索元素。
    棧和佇列很相似,但它執行對元素進行後進先出進行檢索。

  5. 什麼是雙親委派模型?

    當需要載入一個類的時候,子類載入器並不會馬上去載入,而是依次去請求父類別載入器載入,一直往上請求到最高類載入器:啓動類載入器。當啓動類載入器載入不了的時候,依次往下讓子類載入器進行載入。當達到最底下的時候,如果還是載入不到該類,就會出現ClassNotFound的情況。

    好處:保證了程式的安全性。例子:比如我們重新寫了一個String類,載入的時候並不會去載入到我們自己寫的String類,因爲當請求上到最高層的時候,啓動類載入器發現自己能夠載入String類,因此就不會載入到我們自己寫的String類了。

  6. 說一下類載入的執行過程?

    類裝載分爲以下 5 個步驟:
    載入:根據查詢路徑找到相應的 class 檔案然後匯入;
    檢查:檢查載入的 class 檔案的正確性;
    準備:給類中的靜態變數分配記憶體空間;
    解析:虛擬機器將常數池中的符號參照替換成直接參照的過程。符號參照就理解爲一個標示,而在直接參照直接指向記憶體中的地址;
    初始化:對靜態變數和靜態程式碼塊執行初始化工作。

  7. 怎麼判斷物件是否可以被回收?

    一般有兩種方法來判斷:
    參照計數器:爲每個物件建立一個參照計數,有物件參照時計數器 +1,參照被釋放時計數 -1,當計數器爲 0 時就可以被回收。它有一個缺點不能解決回圈參照的問題;
    可達性分析:從 GC Roots 開始向下搜尋,搜尋所走過的路徑稱爲參照鏈。當一個物件到 GC Roots 沒有任何參照鏈相連時,則證明此物件是可以被回收的

  8. java中都有哪些參照型別?

    1. 強參照:發生GC的時候不會被回收。
    2. 軟參照:有用但不是必須的物件,在發生記憶體溢位之前會被回收。
    3. 弱參照:有用但不是必須的物件,在下一次GC的時候會被回收。
    4. 虛參照:無法通過虛參照獲得物件,用PhantomReference實現虛參照,虛參照的用途是在GC時返回一個通知。
  9. 說一下JVM有哪些垃圾回收演算法?

    常用的垃圾回收演算法有如下四種:標記-清除、複製、標記-整理和分代收集。

    標記-清除演算法
    從演算法的名稱上可以看出,這個演算法分爲兩部分,標記和清除。首先標記出所有需要被回收的物件,然後在標記完成後統一回收掉所有被標記的物件。

    這個演算法簡單,但是有兩個缺點:一是標記和清除的效率不是很高;二是標記和清除後會產生很多的記憶體碎片,導致可用的記憶體空間不連續,當分配大物件的時候,沒有足夠的空間時不得不提前觸發一次垃圾回收。

    複製演算法
    這個演算法將可用的記憶體空間分爲大小相等的兩塊,每次只是用其中的一塊,當這一塊被用完的時候,就將還存活的物件複製到另一塊中,然後把原已使用過的那一塊記憶體空間一次回收掉。這個演算法常用於新生代的垃圾回收。

    複製演算法解決了標記-清除演算法的效率問題,以空間換時間,但是當存活物件非常多的時候,複製操作效率將會變低,而且每次只能使用一半的記憶體空間,利用率不高。

    標記-整理演算法
    這個演算法分爲三部分:一是標記出所有需要被回收的物件;二是把所有存活的物件都向一端移動;三是把所有存活物件邊界以外的記憶體空間都回收掉。

    標記-整理演算法解決了複製演算法多複製效率低、空間利用率低的問題,同時也解決了記憶體碎片的問題。

    分代收集演算法
    根據物件生存週期的不同將記憶體空間劃分爲不同的塊,然後對不同的塊使用不同的回收演算法。一般把Java堆分爲新生代和老年代,新生代中物件的存活週期短,只有少量存活的物件,所以可以使用複製演算法,而老年代中物件存活時間長,而且物件比較多,所以可以採用標記-清除和標記-整理演算法。

  10. 說一下JVM中有哪些垃圾回收器?

    **新生代收集器:**Serial、ParNew、Parallel Scavenge
    **老年代收集器:**Serial Old、CMS、Parallel Old
    **堆記憶體垃圾收集器:**G1

    Serial:最早的單執行緒序列垃圾回收器。

    Serial Old:Serial 垃圾回收器的老年版本,同樣也是單執行緒的,可以作爲 CMS 垃圾回收器的備選預案。

    ParNew:是 Serial 的多執行緒版本。 Parallel 和 ParNew 收集器類似是多執行緒的,但 Parallel 是吞吐量優先的收集器,可以犧牲等待時間換取系統的吞吐量。

    Parallel Old 是 Parallel 老生代版本,Parallel 使用的是複製的記憶體回收演算法,Parallel Old 使用的是標記-整理的記憶體回收演算法。

    CMS:一種以獲得最短停頓時間爲目標的收集器,非常適用 B/S 系統。

    G1:一種兼顧吞吐量和停頓時間的 GC 實現,是 JDK 9 以後的預設 GC 選項。

  11. 詳細介紹一下CMS垃圾回收器?

    CMS是基於「標記——清除」演算法實現的,整個過程分爲4個步驟:

    • 初始標記(CMS initial mark);
    • 併發標記(CMS concurrent mark);
    • 重新標記(CMS remark);
    • 併發清除(CMS concurrent sweep)。

    優點:併發收集、低停頓,oracle公司的一些官方文件中也稱之爲併發低停頓收集器(Concurrent Low Pause Collector)【一定要知道:停頓,停頓的是使用者執行緒】

    缺點:

    • CMS收集器對CPU資源非常敏感;
    • CMS收集器無法處理浮動垃圾(Floating Garbage)[併發清理階段使用者執行緒還在執行,這段時間就可能產生新的垃圾,新的垃圾在此次GC無法清除,只能等到下次清理(本來是垃圾,但GC認爲不是垃圾沒有回收)],可能出現「Concurrent Mode Failure」失敗而導致Full GC的產生。如果在應用中老年代增長不是太快,可以適當調高參數 -XX:CMSInitiatingOccupancyFraction 的值來提高觸發百分比,以便降低記憶體回收次數從而獲取更好的效能。要是CMS執行期間預留的記憶體無法滿足程式需要時,虛擬機器將啓動後備預案:臨時啓用 Serial Old收集器來重新進行老年代的垃圾收集,這樣停頓時間就很長了。所以說參數 -XX:CMSInitiatingOccupancyFraction 設定得太高很容易導致大量「Concurrent Mode Failure」失敗,效能反而降低。
    • 收集結束時會有大量空間碎片產生,空間碎片過多時,將會給大物件分配帶來很大麻煩,往往出現老年代還有很大空間剩餘,但是無法找到足夠的連續空間來分配當前物件,不得不提前進行一次Full GC。CMS收集器提供了一個 -XX:+UseCMSCompactAtFullCollection開關參數(預設就是開啓的),用於在CMS收集器頂不住要進行Full GC時開啓記憶體碎片的合併整理過程,記憶體整理的過程無法併發的,空間碎片問題沒有了,但停頓時間不得不變長。
  12. 新生代垃圾回收器和老生代垃圾回收器都有哪些?有什麼區別?

    新生代回收器:Serial、ParNew、Parallel Scavenge

    老年代回收器:Serial Old、Parallel Old、CMS

    整堆回收器:G1

    新生代垃圾回收器一般採用的是複製演算法,複製演算法的優點是效率高,缺點是記憶體利用率低;老年代回收器一般採用的是標記-整理的演算法進行垃圾回收。

  13. 簡述一下分代垃圾回收器是怎麼工作的?

    分代回收器有兩個分割區:老生代和新生代,新生代預設的空間佔比總空間的 1/3,老生代的預設佔比是 2/3。 新生代使用的是複製演算法,新生代裡有 3 個分割區:Eden、To Survivor、From Survivor,它們的預設佔比是 8:1:1,

    它的執行流程如下:

    把 Eden + From Survivor 存活的物件放入 To Survivor 區;

    清空 Eden 和 From Survivor 分割區; From Survivor 和 To Survivor 分割區交換,From Survivor 變 To Survivor,To Survivor 變 From Survivor。

    每次在 From Survivor 到 To Survivor 移動時都存活的物件,年齡就 +1,當年齡到達 15(預設設定是 15)時,升級爲老生代。大物件也會直接進入老生代。

    老生代當空間佔用到達某個值之後就會觸發全域性垃圾收回,一般使用標記整理的執行演算法。

    以上這些循環往復就構成了整個分代垃圾回收的整體執行流程。

  14. 說一下JVM調優的工具?

    *Jconsole : jdk自帶,功能簡單,但是可以在系統有一定負荷的情況下使用。對垃圾回收演算法有很詳細的跟蹤。*

    *JProfiler:商業軟體,需要付費。功能強大。*

    *VisualVM:JDK自帶,功能強大,與JProfiler類似。推薦。*

  15. 常用的JVM調優參數都有哪些?

    -Xms2g:初始化推大小爲 2g;
    -Xmx2g:堆最大記憶體爲 2g;
    -XX:NewRatio=4:設定年輕的和老年代的記憶體比例爲 1:4;
    -XX:SurvivorRatio=8:設定新生代 Eden 和 Survivor 比例爲 8:2;
    –XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器組合;
    -XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器組合;
    -XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器組合;
    -XX:+PrintGC:開啓列印 gc 資訊;
    -XX:+PrintGCDetails:列印 gc 詳細資訊。

MySQL部分面試題

  1. 數據庫的三範式是什麼?

    第一範式:強調的是列的原子性,即數據庫表的每一列都是不可分割的原子數據項。
    第二範式:要求實體的屬性完全依賴於主關鍵字。所謂完全依賴是指不能存在僅依賴主關鍵字一部分的屬性。
    第三範式:任何非主屬性不依賴於其它非主屬性

    一張自增表裏面總共有 7 條數據,刪除了最後 2 條數據,重新啓動 MySQL 數據庫,又插入了一條數據,此時 id 是幾?
    表型別如果是 MyISAM ,那 id 就是 8。
    表型別如果是 InnoDB,那 id 就是 6。
    InnoDB 表只會把自增主鍵的最大 id 記錄在記憶體中,所以重新啓動之後會導致最大 id 丟失。

  2. 如何獲取當前數據庫版本?

    使用 select version() 命令獲取當前 MySQL 數據庫版本。

  3. 說一下ACID是什麼?

    1. Atomicity   原子性:是不可分割的最小操作單位,要麼同時成功,要麼同時失敗。
    2. Consistency 一致性:事務操作前後,數據總量不變
    3. Isolation   隔離性:多個事務之間。相互獨立。
    4. Durability  永續性:當事務提交或回滾後,數據庫會持久化的儲存數據。
    
  4. char和vachar的區別是什麼?

    char(n) :固定長度型別,比如訂閱 char(10),當你輸入"abc"三個字元的時候,它們佔的空間還是 10 個位元組,其他 7 個是空位元組。
    chat 優點:效率高;缺點:佔用空間;適用場景:儲存密碼的 md5 值,固定長度的,使用 char 非常合適。
    varchar(n) :可變長度,儲存的值是每個值佔用的位元組再加上一個用來記錄其長度的位元組的長度。
    所以,從空間上考慮 varcahr 比較合適;從效率上考慮 char 比較合適,二者使用需要權衡。

  5. flioat和double的區別是什麼?

    float 最多可以儲存 8 位的十進制數,並在記憶體中佔 4 位元組。
    double 最可可以儲存 16 位的十進制數,並在記憶體中佔 8 位元組。

  6. mysql的內連線和左右連線有什麼區別?

    內連線關鍵字:inner join;左連線:left join;右連線:right join。
    內連線是把匹配的關聯數據顯示出來;左連線是左邊的表全部顯示出來,右邊的表顯示出符合條件的數據;右連線正好相反

  7. mysql索引是怎麼實現的?

    索引是滿足某種特定查詢演算法的數據結構,而這些數據結構會以某種方式指向數據,從而實現高效查詢數據。
    具體來說 MySQL 中的索引,不同的數據引擎實現有所不同,但目前主流的數據庫引擎的索引都是 B+ 樹實現的,B+ 樹的搜尋效率,可以到達二分法的效能,找到數據區域之後就找到了完整的數據結構了,所有索引的效能也是更好的。

  8. 怎麼驗證mysql索引是否滿足需求?

    使用 explain 檢視 SQL 是如何執行查詢語句的,從而分析你的索引是否滿足需求。
    explain 語法:explain select * from table where type=1。

  9. 說一下數據庫的事務隔離?

     * 概念:多個事務之間隔離的,相互獨立的。但是如果多個事務操作同一批數據,則會引發一些問題,設定不同的隔離級別就可以解決這些問題。
        * 存在問題:
            1. 髒讀:一個事務,讀取到另一個事務中沒有提交的數據
            2. 不可重複讀(虛讀):在同一個事務中,兩次讀取到的數據不一樣。
            3. 幻讀:一個事務操作(DML)數據表中所有記錄,另一個事務新增了一條數據,則第一個事務查詢不到自己的修改。
        * 隔離級別:
            1. read uncommitted:讀未提交
                * 產生的問題:髒讀、不可重複讀、幻讀
            2. read committed:讀已提交 (Oracle)
                * 產生的問題:不可重複讀、幻讀
            3. repeatable read:可重複讀 (MySQL預設)
                * 產生的問題:幻讀
            4. serializable:序列化
                * 可以解決所有的問題
    
            * 注意:隔離級別從小到大安全性越來越高,但是效率越來越低
    
            * 數據庫查詢隔離級別:
                * select @@tx_isolation;
            * 數據庫設定隔離級別:
                * set global transaction isolation level  級別字串;
    
  10. 說一下mysql常用引擎?

    InnoDB 引擎:mysql 5.1 後預設的數據庫引擎,提供了對數據庫 acid 事務的支援,並且還提供了行級鎖和外來鍵的約束,它的設計的目標就是處理大數據容量的數據庫系統。MySQL 執行的時候,InnoDB 會在記憶體中建立緩衝池,用於緩衝數據和索引。但是該引擎是不支援全文搜尋,同時啓動也比較的慢,它是不會儲存表的行數的,所以當進行 select count(*) from table 指令的時候,需要進行掃描全表。由於鎖的粒度小,寫操作是不會鎖定全表的,所以在併發度較高的場景下使用會提升效率的。

    MyIASM 引擎:不提供事務的支援,也不支援行級鎖和外來鍵。因此當執行插入和更新語句時,即執行寫操作的時候需要鎖定這個表,所以會導致效率會降低。不過和 InnoDB 不同的是,MyIASM 引擎是儲存了表的行數,於是當進行 select count(*) from table 語句時,可以直接的讀取已經儲存的值而不需要進行掃描全表。所以,如果表的讀操作遠遠多於寫操作時,並且不需要事務的支援的,可以將 MyIASM 作爲數據庫引擎的首選。

  11. 說一下mysql的行鎖和表鎖?

    MyISAM 只支援表鎖,InnoDB 支援表鎖和行鎖,預設爲行鎖
    表級鎖:開銷小,加鎖快,不會出現死鎖。鎖定粒度大,發生鎖衝突的概率最高,併發量最低。
    行級鎖:開銷大,加鎖慢,會出現死鎖。鎖力度小,發生鎖衝突的概率小,併發度最高。

  12. 說一下樂觀鎖和悲觀鎖?

    樂觀鎖:每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在提交更新的時候會判斷一下在此期間別人有沒有去更新這個數據。
    悲觀鎖:每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻止,直到這個鎖被釋放。
    數據庫的樂觀鎖需要自己實現,在表裏面新增一個 version 欄位,每次修改成功值加 1,這樣每次修改的時候先對比一下,自己擁有的 version 和數據庫現在的 version 是否一致,如果不一致就不修改,這樣就實現了樂觀鎖。

  13. mysql問題排查都有哪些手段?

    使用 show processlist 命令檢視當前所有連線資訊。
    使用 explain 命令查詢 SQL 語句執行計劃。
    開啓慢查詢日誌,檢視慢查詢的 SQL。

  14. 如何做到mysql的效能調優?

    爲搜尋欄位建立索引。
    避免使用 select *,列出需要查詢的欄位。
    垂直分割分表。
    選擇正確的儲存引擎。

Redis部分

  1. redis是什麼?都有哪些使用場景?

    Redis 是一個使用 C 語言開發的快取記憶體數據庫。
    Redis 使用場景:

    • 記錄貼文點贊數、點選數、評論數;
    • 快取近期熱帖;
    • 快取文章詳情資訊;
    • 記錄使用者對談資訊。
  2. redis爲什麼是單執行緒的?

    因爲Redis是基於記憶體的操作,CPU不是Redis的瓶頸,Redis的瓶頸最有可能是機器記憶體的大小或者網路頻寬。既然單執行緒容易實現,而且CPU不會成爲瓶頸,那就順理成章地採用單執行緒的方案了。

  3. 什麼是快取穿透?怎麼解決?

    快取穿透:指查詢一個一定不存在的數據,由於快取是不命中時需要從數據庫查詢,查不到數據則不寫入快取,這將導致這個不存在的數據每次請求都要到數據庫去查詢,造成快取穿透。
    解決方案:最簡單粗暴的方法如果一個查詢返回的數據爲空(不管是數據不存在,還是系統故障),我們就把這個空結果進行快取,但它的過期時間會很短,最長不超過五分鐘。

  4. redis支援的數據型別有哪些?

    Redis 支援的數據型別:string(字串)、list(列表)、hash(字典)、set(集合)、zset(有序集合)。

  5. redis支援的java用戶端都有哪些?

    支援的 Java 用戶端有 Redisson、jedis、lettuce 等。

  6. jedis和Redission有什麼區別?

    jedis:提供了比較全面的 Redis 命令的支援。
    Redisson:實現了分佈式和可延伸的 Java 數據結構,與 jedis 相比 Redisson 的功能相對簡單,不支援排序、事務、管道、分割區等 Redis 特性。

  7. 怎麼保證快取和數據庫數據的一致性?

    合理設定快取的過期時間。
    新增、更改、刪除數據庫操作時同步更新 Redis,可以使用事物機制 機製來保證數據的一致性。

  8. redis持久化有幾種方式?

    Redis 的持久化有兩種方式,或者說有兩種策略:
    RDB(Redis Database):指定的時間間隔能對你的數據進行快照儲存。
    AOF(Append Only File):每一個收到的寫命令都通過write函數追加到檔案中。

  9. redis怎麼實現分佈式鎖?

    Redis 分佈式鎖其實就是在系統裏面佔一個「坑」,其他程式也要佔「坑」的時候,佔用成功了就可以繼續執行,失敗了就只能放棄或稍後重試。
    佔坑一般使用 setnx(set if not exists)指令,只允許被一個程式佔有,使用完呼叫 del 釋放鎖。

  10. redis實現分佈式鎖有什麼缺陷?

    Redis 分佈式鎖不能解決超時的問題,分佈式鎖有一個超時時間,程式的執行如果超出了鎖的超時時間就會出現問題。

  11. redis如何做記憶體優化?

    儘量使用 Redis 的雜湊表,把相關的資訊放到雜湊表裏面儲存,而不是把每個欄位單獨儲存,這樣可以有效的減少記憶體使用。比如將 Web 系統的使用者物件,應該放到雜湊表裏面再整體儲存到 Redis,而不是把使用者的姓名、年齡、密碼、郵箱等欄位分別設定 key 進行儲存。

  12. redis淘汰策略有哪些?

    volatile-lru:從已設定過期時間的數據集(server. db[i]. expires)中挑選最近最少使用的數據淘汰。
    volatile-ttl:從已設定過期時間的數據集(server. db[i]. expires)中挑選將要過期的數據淘汰。
    volatile-random:從已設定過期時間的數據集(server. db[i]. expires)中任意選擇數據淘汰。
    allkeys-lru:從數據集(server. db[i]. dict)中挑選最近最少使用的數據淘汰。
    allkeys-random:從數據集(server. db[i]. dict)中任意選擇數據淘汰。
    no-enviction(驅逐):禁止驅逐數據。

  13. redis常見的效能問題有哪些?該如何解決?

    主伺服器寫記憶體快照,會阻塞主執行緒的工作,當快照比較大時對效能影響是非常大的,會間斷性暫停服務,所以主伺服器最好不要寫記憶體快照。
    Redis 主從複製的效能問題,爲了主從複製的速度和連線的穩定性,主從庫最好在同一個區域網內。

RabbitMQ部分

  1. RabbitMQ的使用場景有哪些?

    搶購活動,削峯填谷,防止系統崩塌。
    延遲資訊處理,比如 10 分鐘之後給下單未付款的使用者發送郵件提醒。
    解耦系統,對於新增的功能可以單獨寫模組擴充套件,比如使用者確認評價之後,新增了給使用者返積分的功能,這個時候不用在業務程式碼裡新增新增積分的功能,只需要把新增積分的介面訂閱確認評價的訊息佇列即可,後面再新增任何功能只需要訂閱對應的訊息佇列即可。

  2. RabbitMQ有哪些重要角色?

    RabbitMQ 中重要的角色有:生產者、消費者和代理:
    生產者:訊息的建立者,負責建立和推播數據到訊息伺服器;
    消費者:訊息的接收方,用於處理數據和確認訊息;
    代理:就是 RabbitMQ 本身,用於扮演「快遞」的角色,本身不生產訊息,只是扮演「快遞」的角色。

  3. RabbitMQ有哪些重要的元件?

    ConnectionFactory(連線管理器):應用程式與Rabbit之間建立連線的管理器,程式程式碼中使用。
    Channel(通道):訊息推播使用的通道。
    Exchange(交換器):用於接受、分配訊息。
    Queue(佇列):用於儲存生產者的訊息。
    RoutingKey(路由鍵):用於把生成者的數據分配到交換器上。
    BindingKey(系結鍵):用於把交換器的訊息系結到佇列上。

  4. RabbitMQ中vhost的作用是什麼?

    vhost:每個 RabbitMQ 都能建立很多 vhost,我們稱之爲虛擬主機,每個虛擬主機其實都是 mini 版的RabbitMQ,它擁有自己的佇列,交換器和系結,擁有自己的許可權機制 機製。

  5. RabbitMQ中的訊息是怎麼發送的?

    首先用戶端必須連線到 RabbitMQ 伺服器才能 纔能發佈和消費訊息,用戶端和 rabbit server 之間會建立一個 tcp 連線,一旦 tcp 開啓並通過了認證(認證就是你發送給 rabbit 伺服器的使用者名稱和密碼),你的用戶端和 RabbitMQ 就建立了一條 amqp 通道(channel),通道是建立在「真實」 tcp 上的虛擬連線,amqp 命令都是通過通道發送出去的,每個通道都會有一個唯一的 id,不論是發佈訊息,訂閱佇列都是通過這個通道完成的。

  6. RabbitMQ怎麼保證訊息的穩定性?

    提供了事務的功能。
    通過將 channel 設定爲 confirm(確認)模式

  7. RabbitMQ怎麼避免訊息丟失?

    把訊息持久化磁碟,保證伺服器重新啓動訊息不丟失。
    每個叢集中至少有一個物理磁碟,保證訊息落入磁碟。

  8. 要保證訊息持久化成功的條件有哪些?

    宣告佇列必須設定持久化 durable 設定爲 true.
    訊息推播投遞模式必須設定持久化,deliveryMode 設定爲 2(持久)。
    訊息已經到達持久化交換器。
    訊息已經到達持久化佇列。
    以上四個條件都滿足才能 纔能保證訊息持久化成功。

  9. RabbitMQ持久化有什麼缺點?

    持久化的缺點就是降低了伺服器的吞吐量,因爲使用的是磁碟而非記憶體儲存,從而降低了吞吐量。可儘量使用 ssd 硬碟來緩解吞吐量的問題。

  10. RabbitMQ有幾種廣播型別?

    direct(預設方式):最基礎最簡單的模式,發送方把訊息發送給訂閱方,如果有多個訂閱者,預設採取輪詢的方式進行訊息發送。
    headers:與 direct 類似,只是效能很差,此型別幾乎用不到。
    fanout:分發模式,把消費分發給所有訂閱者。
    topic:匹配訂閱模式,使用正則匹配到訊息佇列,能匹配到的都能接收到。

  11. RabbitMQ 怎麼實現延遲訊息佇列?

    延遲佇列的實現有兩種方式:
    通過訊息過期後進入死信交換器,再由交換器轉發到延遲消費佇列,實現延遲功能;
    使用 RabbitMQ-delayed-message-exchange 外掛實現延遲功能。

  12. RabbitMQ節點的型別有哪些?

    磁碟節點:訊息會儲存到磁碟。
    記憶體節點:訊息都儲存在記憶體中,重新啓動伺服器訊息丟失,效能高於磁碟型別。

  13. RabbitMQ叢集搭建需要注意哪些問題?

    各節點之間使用「–link」連線,此屬性不能忽略。
    各節點使用的 erlang cookie 值必須相同,此值相當於「祕鑰」的功能,用於各節點的認證。
    整個叢集中必須包含一個磁碟節點。

  14. RabbitMQ每個節點是其他節點的完整拷貝嗎?爲什麼?

    不是,原因有以下兩個:
    儲存空間的考慮:如果每個節點都擁有所有佇列的完全拷貝,這樣新增節點不但沒有新增儲存空間,反而增加了更多的冗餘數據;
    效能的考慮:如果每條訊息都需要完整拷貝到每一個叢集節點,那新增節點並沒有提升處理訊息的能力,最多是保持和單節點相同的效能甚至是更糟。

  15. RabbitMQ叢集中唯一一個磁碟節點崩潰了會發生什麼情況?

    如果唯一磁碟的磁碟節點崩潰了,不能進行以下操作:
    不能建立佇列
    不能建立交換器
    不能建立系結
    不能新增使用者
    不能更改許可權
    不能新增和刪除叢集節點
    唯一磁碟節點崩潰了,叢集是可以保持執行的,但你不能更改任何東西。

  16. RabbitMQ對叢集節點停止順序有要求嗎?

    RabbitMQ 對叢集的停止的順序是有要求的,應該先關閉記憶體節點,最後再關閉磁碟節點。如果順序恰好相反的話,可能會造成訊息的丟失。

zookeeper部分

  1. zookeeper 是什麼?
    zookeeper 是一個分佈式的,開放原始碼的分佈式應用程式協調服務,是 google chubby 的開源實現,是 hadoop 和 hbase 的重要元件。它是一個爲分佈式應用提供一致性服務的軟體,提供的功能包括:設定維護、域名服務、分佈式同步、組服務等。

  2. zookeeper 都有哪些功能?

    叢集管理:監控節點存活狀態、執行請求等。
    主節點選舉:主節點掛掉了之後可以從備用的節點開始新一輪選主,主節點選舉說的就是這個選舉的過程,使用 zookeeper 可以協助完成這個過程。
    分佈式鎖:zookeeper 提供兩種鎖:獨佔鎖、共用鎖。獨佔鎖即一次只能有一個執行緒使用資源,共用鎖是讀鎖共用,讀寫互斥,即可以有多線執行緒同時讀同一個資源,如果要使用寫鎖也只能有一個執行緒使用。zookeeper可以對分佈式鎖進行控制。
    命名服務:在分佈式系統中,通過使用命名服務,用戶端應用能夠根據指定名字來獲取資源或服務的地址,提供者等資訊。

  3. zookeeper 有幾種部署模式?

    ookeeper 有三種部署模式:
    單機部署:一臺叢集上執行;
    叢集部署:多臺叢集執行;
    僞叢集部署:一臺叢集啓動多個 zookeeper 範例執行。

  4. zookeeper 怎麼保證主從節點的狀態同步?

    zookeeper 的核心是原子廣播,這個機制 機製保證了各個 server 之間的同步。實現這個機制 機製的協定叫做 zab 協定。 zab 協定有兩種模式,分別是恢復模式(選主)和廣播模式(同步)。當服務啓動或者在領導者崩潰後,zab 就進入了恢復模式,當領導者被選舉出來,且大多數 server 完成了和 leader 的狀態同步以後,恢復模式就結束了。狀態同步保證了 leader 和 server 具有相同的系統狀態。

  5. 叢集中爲什麼要有主節點?

    在分佈式環境中,有些業務邏輯只需要叢集中的某一臺機器進行執行,其他的機器可以共用這個結果,這樣可以大大減少重複計算,提高效能,所以就需要主節點。

  6. 叢集中有 3 台伺服器,其中一個節點宕機,這個時候 zookeeper 還可以使用嗎?

    可以繼續使用,單數伺服器只要沒超過一半的伺服器宕機就可以繼續使用。

  7. 說一下 zookeeper 的通知機制 機製?

    用戶端端會對某個 znode 建立一個 watcher 事件,當該 znode 發生變化時,這些用戶端會收到 zookeeper 的通知,然後用戶端可以根據 znode 變化來做出業務上的改變。

kafka 部分

  1. kafka 可以脫離 zookeeper 單獨使用嗎?爲什麼?

    kafka 不能脫離 zookeeper 單獨使用,因爲 kafka 使用 zookeeper 管理和協調 kafka 的節點伺服器。

  2. kafka 有幾種數據保留的策略?

    kafka 有兩種數據儲存策略:按照過期時間保留和按照儲存的訊息大小保留。

    kafka 同時設定了 7 天和 10G 清除數據,到第五天的時候訊息達到了 10G,這個時候 kafka 將如何處理?
    這個時候 kafka 會執行數據清除工作,時間和大小不論哪個滿足條件,都會清空數據。

  3. 什麼情況會導致 kafka 執行變慢?

    • cpu 效能瓶頸
    • 磁碟讀寫瓶頸
    • 網路瓶頸
  4. 使用 kafka 叢集需要注意什麼?

    叢集的數量不是越多越好,最好不要超過 7 個,因爲節點越多,訊息複製需要的時間就越長,整個羣組的吞吐量就越低。
    叢集數量最好是單數,因爲超過一半故障叢集就不能用了,設定爲單數容錯率更高。