面試題
- 零、開場介紹
- 一、JAVA基礎
- 1. Java和C++,C#區別
- 2. 面向物件和面向程序的區別
- 3. JDK,JRE區別
- 4. ==和 equals 的區別?
- 5. 為什么重寫equals還要重寫hashcode?
- 6. 說說抽象類和介面
- 7. String,StringBuffer和StringBuilder,
- 8. final在java中的作用,
- 9. final修飾的物件什么時候被初始化?
- 10. final finally finalize()區別
- 11. 什么是反射?有什么作用?
- 12.常見的例外類有哪些?
- 13. Java如何序列化?
- 14.你知道java8的新特性嗎,請簡單介紹一下?
- 15. 什么是多型?如何實作?有什么好處?
- 二、JAVA容器
- 1. 集合了解吧,說說集合有幾大類,分別介紹一下
- 2. hashmap和concurenthashmap區別
- 3. Array,ArrayList和LinkedList的區別?ArrayList如何擴容?
- 4. hashMap底層實作了解過嗎?具體講講
- 5. 說說hashMap的jdk1.8的優化
- 6. HashMap 和 hashTable的區別?
- 7. HashSet和HashMap有什么區別?
- 8. 說說ConcurrentHashMap的底層實作
- 9. Jdk中map的實作都有什么:
- 10. LinkedHashMap跟HashMap的關系:
- 11. 紅黑樹和完全平衡二叉樹(AVL)
- 三、多執行緒
- 1.為什么要使用多執行緒?多執行緒可能出現什么問題 ?
- 2. java實作多執行緒的方式有幾種?
- 3. Runnable和Callable有什么區別?
- 4. 執行緒和行程的區別
- 5. 什么是守護執行緒?
- 6. java的執行緒大概有幾種狀態?
- 7. 說說與執行緒相關的方法
- 8. sleep 和 wait方法的區別?
- 9. 執行緒的 run() 和 start() 有什么區別?
- 10. sleep()和yield()有什么區別?
- 11. 死鎖的四個條件?
- 12. 怎么在開發中避免死鎖?
- 13. 怎么檢測死鎖?
- 14. 怎么解決死鎖?
- 15.執行緒安全是什么?如何保證執行緒安全?
- 16. 10個執行緒,一個執行緒出錯,怎么通知其它的執行緒,
- 17. 如何避免指令重排序
- 18. volatile除了避免指令重排序還有什么功能
- 19. 說說volatile關鍵字
- 20. ThreadLocal有什么作用?有哪些使用場景?
- 21. 高并發下,如何安全地修改同一行資料?
- 22. synchronized 和 volatile 的區別是什么?
- 23. synchronized 和 Lock 有什么區別?
- 24. synchronized 和 ReentrantLock 區別是什么?
- 四、計算機網路
- 1. get 和 post的區別
- 2. TCP和UDP的區別?tcp擁塞控制和流量控制如何實作?
- 3. 輸入一次url程序,用到哪些協議?
- 5. HTTP和HTTPS的區別,
- 6. HTTPS加密認證程序,
- 7. TCP的三次握手,四次揮手
- 五、JVM
- 1. GC標記方法,
- 2. finalize方法,
- 3. 說一下垃圾回識訓制?什么時候垃圾回收?
- 4. 堆記憶體和堆疊記憶體有什么區別?堆和堆疊哪個快?什么變數存在堆疊里面?
- 5. Java中類加載程序是什么樣的?
- 6. JVM 如何確定垃圾物件:
- 7. 回收演算法
- 8. 說說JVM記憶體區域分為幾大塊,分別講一下
- 六、資料庫
- 1. 說說mysql的存盤引擎
- 2. 講下索引以及應用場景
- 3. 索引的作用?索引有什么缺點?
- 4. 創建索引的原則
- 5. 為什么MySQL 沒有使用Hash作為索引的資料結構呢?
- 6. MySQL索引使用的什么資料結構,B樹和B+樹的區別
- 7. 索引為什么采用B+樹的資料結構,而不使用二叉樹或者紅黑樹
- 8. 聚簇索引和非聚簇索引這兩個概念怎么理解?
- 9. mysql索引優化相關方法,聯合索引應該把什么欄位放在第一個位置?
- 10. mysql的最左原則嗎?
- 11. 臟讀、幻讀、不可重復讀指什么?
- 12. 資料庫事務的四個特性:
- 13. 說說sql的事務隔離級別,具體的應用場景
- 14. 說說資料庫的樂觀鎖和悲觀鎖?
- 七、設計模式
- 1. 你最熟悉的設計模式 ?
- 2. 懶漢式你會怎么寫,懶漢式實體化在哪,建構式的權限?
- 3. 單例模式的餓漢式和懶漢式及區別
- 4. 常用的設計模式?
- 5. 簡單工廠和抽象工廠有什么區別?
- 6. 說一說設計模式中的代理模式?
- 7. 說一說設計模式中的配接器模式?
- 八、框架
- 1. Spring的特點?
- 2.談談自己對于 Spring IoC 的了解
- 3.Spring AOP的實作原理?具體應用在哪些方面?舉個例子?
- 4.Spring 框架中用到了哪些設計模式?
- 九、Redis
- 1. redis都有哪些資料結構?
- 2. Redis的應用場景
- 3. redis的延時佇列怎么實作?
- 4. Redis如何實作持久化?
- 5. 什么是Redis的事務,用來干什么?
- 6. Redis是阻塞式IO嗎?怎么做到請求一個一個進行處理?
- 7. Redis快取淘汰策略?是失效時間到了就立即淘汰嗎?淘汰控制?
- 8. 什么是快取雪崩和快取穿透?
- 9. 如何解決 Redis 快取雪崩問題
- 10. 如何解決 Redis 快取穿透問題
- 11. Redis如何實作分布式鎖
- 十、Kafka
- 1. 問一下kafka的問題吧,kafka是怎么進行資料備份的?
- 2. 消費者是從leader中拿資料,還是從follow中拿資料?
- 3. 說說kafka的ISR機制?
- 4. kafka里面存的資料格式都是什么樣的?
- 5. kafka中存的一個是資料檔案,一個是索引檔案,說說這個?
- 6. kafka 是如何清理過期資料的?
- 7. 一條message中包含哪些資訊?
- 十一、專案
- 1. 校園論壇專案介紹
- 2. 這個專案最具挑戰的是什么?
- 3. 專案中如何使用多執行緒?
- 4. 登錄注冊是如何實作的?用Cookie做了什么? Cookie被竊取了該怎么辦?
- 5. Kafka做什么的?
- 6. 專案Kafka使用場景,為什么要用Kafka? Kafka為什么吞吐量高?
- 7. Kafka怎么保證順序性?
- 8. 說一下kafka的內部結構,訊息佇列存在的意義?
- 9. 如何做到顯示首頁的熱度最高帖子?如何更新快取?
- 10. Redis存了什么資料?快取過期時間是多少?如何解決快取一致性問題?
- 11. 如何識別熱點資料?熱度如何計算?如何更新熱度?
- 12. Redis如何淘汰過期資料?
- 13. 專案里redis做持久化處理了嗎
- 14. redis宕機了怎么辦
- 15. zset的底層資料結構?什么是跳表?
- 15. ES如何實作全文搜索的功能?ES的底層資料結構?
- 16. ES倒排索引為什么能夠加速搜索?
- 17. 專案中SpringSecurity的權限模型是怎么樣的?
- 18 .用戶的授權資訊如何存盤?
- 十二、演算法
- 1. 穩定排序有哪些?
- 2. 排序的時空復雜度
零、開場介紹
一、JAVA基礎
1. Java和C++,C#區別
- 都是面向物件的語言,都支持封裝、繼承和多型
- Java 不提供指標來直接訪問記憶體,程式記憶體更加安全
- Java 的類是單繼承的,C++ 支持多重繼承;雖然 Java 的類不可以多繼承,但是介面可以多繼承,
- Java 有自動記憶體管理垃圾回識訓制(GC),不需要程式員手動釋放無用記憶體,
- C ++同時支持方法多載和運算子多載,但是 Java 只支持方法多載(運算子多載增加了復雜性,這與 Java 最初的設計思想不符),
2. 面向物件和面向程序的區別
面向程序: 面向程序性能比面向物件高, 因為類呼叫時需要實體化,開銷比較大,比較消耗資源,所以當性能是最重要的考量因素的時候,比如單片機、嵌入式開發、Linux/Unix 等一般采用面向程序開發,但是,面向程序沒有面向物件易維護、易復用、易擴展,
面向物件: 面向物件易維護、易復用、易擴展, 因為面向物件有封裝、繼承、多型性的特性,所以可以設計出低耦合的系統,使系統更加靈活、更加易于維護,但是,面向物件性能比面向程序低,
3. JDK,JRE區別
Java運行時環境(JRE)是將要執行Java程式的Java虛擬機,它同時也包含了執行applet需要的瀏覽器插件,Java開發工具包(JDK)是完整的Java軟體開發包,包含了JRE,編譯器和其他的工具(比如:JavaDoc,Java除錯器),可以讓開發者開發、編譯、執行Java應用程式,
4. ==和 equals 的區別?
==比較
- 基本資料型別比較的是值;
- 參考型別比較的是地址值,
equals(Object o):
1)不能比較基本資料型別,基本資料型別不是型別別;
2)比較參考型別時(該方法繼承自Object,在object中比較的是地址值)等同于”==”;
Object類中的方法,所以,在每一個java類中,都會有這個方法,因為每一個java類都是直接或者間接的Object類的子類,會繼承到這個方法,
5. 為什么重寫equals還要重寫hashcode?
如果兩個物件相等,則 hashcode 一定也是相同的,兩個物件相等,對兩個物件分別呼叫 equals 方法都回傳 true,但是,兩個物件有相同的 hashcode 值,它們也不一定是相等的 ,因此,equals 方法被覆寫過,則 hashCode 方法也必須被覆寫,
6. 說說抽象類和介面
- 抽象類可以有構造方法;介面中不能有構造方法,
- 抽象類中可以有普通成員變數;介面中沒有普通成員變數,
- 抽象類中可以包含非抽象普通方法;JDK1.8 以前介面中的所有方法默認都是抽象的,JDK1.8 開始方法可以有 default 實作和 static 方法,
- 抽象類中的抽象方法的訪問權限可以是 public、protected 和 default;介面中的抽象方法只能是 public 型別的,并且默認即為 public abstract 型別,
- 抽象類中可以包含靜態方法;JDK1.8 前介面中不能包含靜態方法,JDK1.8 及以后可以包含已實作的靜態方法,
- 抽象類和介面中都可以包含靜態成員變數,抽象類中的靜態成員變數可以是任意訪問權限;介面中變數默認且只能是 public static final 型別,
- 一個類可以實作多個介面,用逗號隔開,但只能繼承一個抽象類,
- 介面不可以實作介面,但可以繼承介面,并且可以繼承多個介面,用逗號隔開,
7. String,StringBuffer和StringBuilder,
相同點:
- 都可以儲存和操作字串
- 都使用 final 修飾,不能被繼承
- 提供的 API 相似
區別:
- String 是只讀字串,String 物件內容是不能被改變的
- StringBuffer 和 StringBuilder 的字串物件可以對字串內容進行修改,在修改后的記憶體地址不會發生改變
- StringBuilder 執行緒不安全;StringBuffer 執行緒安全
- 方法體內沒有對字串的并發操作,且存在大量字串拼接操作,建議使用 StringBuilder,效率較高,
8. final在java中的作用,
final 語意是不可改變的,
- 被 final 修飾的類,不能夠被繼承
- 被 final 修飾的成員變數必須要初始化,賦初值后不能再重新賦值(可以呼叫物件方法修改屬性值),對基本型別來 說是其值不可變;對參考變數來說其參考不可變,即不能再指向其他的物件
- 被 final 修飾的方法不能重寫
9. final修飾的物件什么時候被初始化?
final型別的靜態變數(即編譯期常量)在類加載時就會被初始化放入常量池中,其他的非編譯期常量是在運行期初始化的,
10. final finally finalize()區別
- final 表示最終的、不可改變的,用于修飾類、方法和變數,final 修飾的類不能被繼承;final 方法也同樣只能使用,不能重寫,但能夠多載;final 修飾的成員變數必須在宣告時給定初值或者在構造方法內設定初始值,只能讀取,不可修改;final 修飾的區域變數必須在宣告時給定初值;final 修飾的變數是非基本型別,物件的參考地址不能變,但物件的屬性值可以改變
- finally 例外處理的一部分,它只能用在 try/catch 陳述句中,表示希望 finally 陳述句塊中的代碼最后一定被執行(存在一些情況導致 finally 陳述句塊不會被執行,如 jvm 結束)
- finalize() 是在 java.lang.Object 里定義的,Object 的 finalize() 方法什么都不做,物件被回收時 finalize() 方法會被呼叫,Java 技術允許使用 finalize() 方法在垃圾收集器將物件從記憶體中清除出去之前做必要清理作業,在垃圾收集器洗掉物件之前被呼叫的,一般情況下,此方法由JVM呼叫,特殊情況下,可重寫 finalize() 方法,當物件被回收的時候釋放一些資源,須呼叫 super.finalize() ,
11. 什么是反射?有什么作用?
Java 反射,就是在運行狀態中
- 獲取任意類的名稱、package 資訊、所有屬性、方法、注解、型別、類加載器、modifiers(public、static)、父類、現實介面等
- 獲取任意物件的屬性,并且能改變物件的屬性
- 呼叫任意物件的方法
- 判斷任意一個物件所屬的類
- 實體化任意一個類的物件
Java 的動態就體現在反射,通過反射我們可以實作動態裝配,降低代碼的耦合度;動態代理等,反射的過度使用會嚴重消耗系統資源,
JDK 中 java.lang.Class 類,就是為了實作反射提供的核心類之一,
一個 jvm 中一種 Class 只會被加載一次,
12.常見的例外類有哪些?
java 中,所有的例外都有一個共同的祖先 java.lang 包中的 Throwable 類,Throwable 類有兩個重要的子類 Exception(例外)和 Error(錯誤),Exception 能被程式本身處理(try-catch), Error 是無法處理的(只能盡量避免),
Exception 和 Error 二者都是 Java 例外處理的重要子類,各自都包含大量子類,
-
Exception :程式本身可以處理的例外,可以通過 catch 來進行捕獲,Exception 又可以分為 受檢查例外(必須處理) 和 不受檢查例外(可以不處理),
-
Error :Error 屬于程式無法處理的錯誤 ,我們沒辦法通過 catch 來進行捕獲 ,例如,Java 虛擬機運行錯誤(Virtual MachineError)、虛擬機記憶體不夠錯誤(OutOfMemoryError)、類定義錯誤(NoClassDefFoundError)等 ,這些例外發生時,Java 虛擬機(JVM)一般會選擇執行緒終止,
13. Java如何序列化?
序列化:將 Java 物件轉換成位元組流的程序,
反序列化:將位元組流轉換成 Java 物件的程序,
當 Java 物件需要在網路上傳輸 或者 持久化存盤到檔案中時,就需要對 Java 物件進行序列化處理,
序列化的實作:類實作 Serializable 介面,這個介面沒有需要實作的方法,實作 Serializable 介面是為了告訴 jvm 這個類的物件可以被序列化,
注意事項:
- 某個類可以被序列化,則其子類也可以被序列化
- 物件中的某個屬性是物件型別,需要序列化也必須實作 Serializable 介面
- 宣告為 static 和 transient 的成員變數,不能被序列化,static 成員變數是描述類級別的屬性,transient 表示臨時資料
- 反序列化讀取序列化物件的順序要保持一致
14.你知道java8的新特性嗎,請簡單介紹一下?
- Lambda 運算式 ? Lambda允許把函式作為一個方法的引數(函式作為引數傳遞進方法中,
- 方法參考? 方法參考提供了非常有用的語法,可以直接參考已有Java類或物件(實體)的方法或構造器,與lambda聯合使用,方法參考可以使語言的構造更緊湊簡潔,減少冗余代碼,
- 默認方法? 默認方法就是一個在介面里面有了一個實作的方法,
- 新工具? 新的編譯工具,如:Nashorn引擎 jjs、 類依賴分析器jdeps,
- Stream API ?新添加的Stream API(java.util.stream) 把真正的函式式編程風格引入到Java中,
- Date Time API ? 加強對日期與時間的處理,
- Optional 類 ? Optional 類已經成為 Java 8 類別庫的一部分,用來解決空指標例外,
- Nashorn, JavaScript 引擎 ? Java 8提供了一個新的Nashorn javascript引擎,它允許我們在JVM上運行特定的javascript應用,
15. 什么是多型?如何實作?有什么好處?
多型:
同一個介面,使用不同的實體而執行不同操作,同一個行為具有多個不同表現形式或形態的能力,
實作多型有三個條件:
- 繼承
- 子類重寫父類的方法
- 父類參考變數指向子類物件
實作多型的技術稱為:動態系結(dynamic binding),是指在執行期間判斷所參考物件的實際型別,根據其實際的型別呼叫其相應的方法,
Java 中使用父類的參考變數呼叫子類重寫的方法,即可實作多型,
二、JAVA容器
1. 集合了解吧,說說集合有幾大類,分別介紹一下
Java 集合, 也叫作容器,主要是由兩大介面派生而來:一個是 Collecton介面,主要用于存放單一元素;另一個是 Map 介面,主要用于存放鍵值對,對于Collection 介面,下面又有三個主要的子介面:List、Set 和 Queue,
- List(對付順序的好幫手): 存盤的元素是有序的、可重復的,
- Set(注重獨一無二的性質): 存盤的元素是無序的、不可重復的,
- Queue(實作排隊功能的叫號機): 按特定的排隊規則來確定先后順序,存盤的元素是有序的、可重復的,
- Map(用 key 來搜索的專家): 使用鍵值對(key-value)存盤,類似于數學上的函式 y=f(x),“x” 代表 key,“y” 代表 value,key 是無序的、不可重復的,value 是無序的、可重復的,每個鍵最多映射到一個值,
2. hashmap和concurenthashmap區別
HashMap和ConcurentHashMap的主要區別是HashMaP是執行緒不安全,ConcurentHashMap是執行緒安全
JDK1.7
(1) HashMap的執行緒不安全主要是發生在擴容函式中,即根源是在transfer函式中,由于采用頭插法,在多執行緒高并發環境下會造成死回圈或資料丟失問題,
(2) ConcurentHashMap采用分段鎖,可重入鎖Segment類,首先將資料分成一段一段的存盤,然后給每一段資料配一把鎖,當一個執行緒占用鎖訪問其中一個段資料的時候,其他段的資料也能被其他執行緒訪問,
JDK1.8
(1) HashMap在JDK 1.8中采用尾插法修復了1.7中由于頭插法引起的執行緒不安全(死回圈和資料丟失),在JDK 1.8中進行put操作會引起執行緒不安全而導致資料覆寫,
(2) ConcurentHashMap采用CAS和synchronized來保證執行緒安全,使用的是鎖分離思想,只是鎖住的是一個node,而鎖住Node之前的操作是基于在volatile和CAS之上無鎖并且執行緒安全的,并且大量使用了U.compareAndSwapXXX的方法,這個方法是利用一個CAS演算法實作,
3. Array,ArrayList和LinkedList的區別?ArrayList如何擴容?
Array 即陣列
定義一個 Array 時,必須指定陣列的資料型別及陣列長度,即陣列中存放的元素個數固定并且型別相同,
ArrayList 是動態陣列,長度動態可變,會自動擴容,不使用泛型的時候,可以添加不同型別元素,
ArrayList和LinkedList的區別
- ArrayList 基于動態陣列實作的非執行緒安全的集合;LinkedList 基于雙向鏈表實作的非執行緒安全的集合,
- 擴容問題:ArrayList 使用陣列實作,無參建構式默認初始化長度為 10,陣列擴容是會將原陣列中的元素重新拷貝到新陣列中,長度為原來的 1.5 倍(擴容代價高);LinkedList 不存在擴容問題,新增元素放到集合尾部,修改相應的指標節點即可,
- LinkedList 比 ArrayList 更占記憶體,因為 LinkedList 為每一個節點存盤了兩個參考節點,一個指向前一個元素,一個指向下一個元素,
- 對于隨機 index 訪問的 get 和 set 方法,一般 ArrayList 的速度要優于 LinkedList,因為 ArrayList 直接通過陣列下標直接找到元素;LinkedList 要移動指標遍歷每個元素直到找到為止,
- 新增和洗掉元素,一般 LinkedList 的速度要優于 ArrayList,因為 ArrayList 在新增和洗掉元素時,可能擴容和復制陣列;LinkedList 實體化物件需要時間外,只需要修改節點指標即可,
- LinkedList 集合不支持高效的隨機訪問(RandomAccess)
- ArrayList 的空間浪費主要體現在在list串列的結尾預留一定的容量空間;LinkedList 的空間花費則體現在它的每一個元素都需要消耗存盤指標節點物件的空間,
- 都是非執行緒安全,允許存放 null
4. hashMap底層實作了解過嗎?具體講講
- HashMap 基于 Hash 演算法實作,通過 put(key,value) 存盤,get(key) 來獲取 value
- 當傳入 key 時,HashMap 會根據 key,呼叫 hash(Object key) 方法,計算出 hash 值,根據 hash 值將 value 保存在 Node 物件里,Node 物件保存在陣列里
- 當計算出的 hash 值相同時,稱之為 hash 沖突,HashMap 的做法是用鏈表和紅黑樹存盤相同 hash 值的 value
- 當 hash 沖突的個數:小于等于 8 使用鏈表;大于 8 且 tab length 大于等于 64 時,使用紅黑樹解決鏈表查詢慢的問題
ps:
- 上述是 JDK 1.8 HashMap 的實作原理,并不是每個版本都相同,比如 JDK 1.7 的 HashMap 是基于陣列 + 鏈表實作,所以 hash 沖突時鏈表的查詢效率低
- hash(Object key) 方法的具體演算法是 (h = key.hashCode()) ^ (h >>> 16),經過這樣的運算,讓計算的 hash 值分布更均勻
5. 說說hashMap的jdk1.8的優化
JDK1.8在JDK1.7的基礎上針對一個鏈上資料過多(即拉鏈過長的情況)導致性能下降,增加了紅黑樹來進行優化,即當鏈表超過8時,鏈表就轉換為紅黑樹,利用紅黑樹快速增刪改查的特點提高HashMap的性能,其中會用到紅黑樹的插入、洗掉、查找等演算法,
6. HashMap 和 hashTable的區別?
JDK 1.8 中 HashMap 和 Hashtable 主要區別如下:
- 執行緒安全性不同,HashMap 執行緒不安全;Hashtable 中的方法是 synchronized 的,
- key、value 是否允許 null,HashMap 的 key 和 value 都是可以是 null,key 只允許一個 null;Hashtable 的 key 和 value 都不可為 null,
- 迭代器不同,HashMap 的 Iterator 是 fail-fast 迭代器;Hashtable 還使用了 enumerator 迭代器,
- hash的計算方式不同,HashMap 計算了 hash值;Hashtable 使用了 key 的 hashCode方法,
- 默認初始大小和擴容方式不同,HashMap 默認初始大小 16,容量必須是 2 的整數次冪,擴容時將容量變為原來的2倍;Hashtable 默認初始大小 11,擴容時將容量變為原來的 2 倍加 1,
- 是否有 contains 方法,HashMap 沒有 contains 方法;Hashtable 包含 contains 方法,類似于 containsValue,
- 父類不同,HashMap 繼承自 AbstractMap;Hashtable 繼承自 Dictionary,
7. HashSet和HashMap有什么區別?
HashMap
- 實作 Map 介面
- 鍵值對的方式存盤
- 新增元素使用 put(K key, V value) 方法
- 底層通過對 key 進行 hash,使用陣列 + 鏈表或紅黑樹對 key、value 存盤
HashSet
- 實作 Set 介面
- 存盤元素物件
- 新增元素使用 add(E e) 方法
- 底層是采用 HashMap 實作,大部分方法都是通過呼叫 HashMap 的方法來實作
8. 說說ConcurrentHashMap的底層實作
ConcurrentHashMap1.7 實作原理
ConcurrentHashMap 采用分段鎖設計、將一個大的 HashMap 集合拆分成 n 多個不同的小的 HashTable(Segment),默認的情況下是分成 16 個不同的 Segment,每個 Segment 中都有自己獨立的 HashEntry<K,V>[] table;
陣列+Segments 分段鎖+HashEntry 鏈表實作
使用 Lock 鎖+CAS 樂觀鎖+UNSAFE 類
PUT 方法流程
- 第一次需要計算出:key 出存放在那個 Segment 物件中
- 還需要計算 key 存放在 Segment 物件中具體 index 位置,
ConcurrentHashMap1.8 實作原理
Put 原理 鎖的粒度非常小,對每個陣列 index 位置上鎖 對 1.7ConcurrentHashMap 實作優化
- 取消 segment 分段設計,使用 synchronized 鎖
- synchronized 在 JDK1.6 開始做了優化 默認實作鎖的升級程序
JDK 1.7 到 JDK 1.8 中的 ConcurrentHashMap 最大的改動:
鏈表上的 Node 超過 8 個改為紅黑樹,查詢復雜度 O(logn)
ReentrantLock 顯示鎖改為 synchronized,說明 JDK 1.8 中 synchronized 鎖性能趕上或超過 ReentrantLock
9. Jdk中map的實作都有什么:
HashMap、TreeMap、Hashtable、LinkedHashMap,
10. LinkedHashMap跟HashMap的關系:
LinkedHashMap維護了一個雙向回圈鏈表,是有序的,保留了元素的插入順序,
11. 紅黑樹和完全平衡二叉樹(AVL)
紅黑樹的關鍵性質: 從根到葉子的最長的可能路徑不多于最短的可能路徑的兩倍長,
平衡二叉樹(AVL)的性質
它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,并且左右兩個子樹都是一棵平衡二叉樹,
區別:
1、紅黑樹放棄了追求完全平衡,追求大致平衡,在與平衡二叉樹的時間復雜度相差不大的情況下,保證每次插入最多只需要三次旋轉就能達到平衡,實作起來也更為簡單,
2、平衡二叉樹追求絕對平衡,條件比較苛刻,實作起來比較麻煩,每次插入新節點之后需要旋轉的次數不能預知
三、多執行緒
1.為什么要使用多執行緒?多執行緒可能出現什么問題 ?
由于創建和銷毀執行緒都需要很大的開銷,運用執行緒池就可以大大的緩解這些記憶體開銷很大的問題;可以根據系統的承受能力,調整執行緒池中作業線執行緒的數目,防止因為消耗過多的記憶體 ,
多執行緒并發編程并不總是能提高程式的執行效率和運行速度,而且可能存在一些問題,包括記憶體泄漏、背景關系切換、死鎖以及受限于硬體和軟體的資源限制問題等,
2. java實作多執行緒的方式有幾種?
有4種方式可以用來創建執行緒:
- 繼承Thread類
class MyThread extends Thread{
public void run(){
System.out.println("執行緒運行");
}
}
public class Test{
public static void main(String[] args){
MyThread thread=new MyThread();
thread.start();//開啟執行緒
}
}
- 實作Runnable介面
class MyThread implements Runnable
{
public void run(){
System.out.println("執行緒運行");
}
}
public class Test{
public static void main(String[] args){
MyThread thread=new MyThread();
Thread t=new Thread(thread);
t.start();//開啟執行緒
}
}
- 還有一種方式是實作Callable介面
實作Runnable介面這種方式更受歡迎,因為這不需要繼承Thread類,在應用設計中已經繼承了別的物件的情況下,這需要多繼承(而Java不支持多繼承),只能實作介面,同時,執行緒池也是非常高效的,很容易實作和使用,
import java.util.concurrent.*;
public class CallableAndFuture{
//創建執行緒
public static class CallableTest implements Callable<String>{
public String call() throws Exception{
return "Hello World";
}
}
public static void main(String[] args){
ExecutorService threadPool=Executors.newSingleThreadExecutor();
//啟動執行緒
Future<String> future=threadPool.submit(new CallableTest());
try{
System.out.println("等待執行緒執行完成");
System.out.println(future.get());//等待執行緒結束,并獲取回傳結果
}
catch(Exception e){
e.printStackTrace();
}
}
}
3. Runnable和Callable有什么區別?
主要區別
- Runnable 介面 run 方法無回傳值;Callable 介面 call 方法有回傳值,支持泛型
- Runnable 介面 run 方法只能拋出運行時例外,且無法捕獲處理;Callable 介面 call 方法允許拋出例外,可以獲取例外資訊
4. 執行緒和行程的區別
-
行程是程式的一次執行程序,是系統運行程式的基本單位,因此行程是動態的,系統運行一個程式即為一個行程的創建、運行以及消亡的程序,
-
執行緒是比行程更小的執行單位,一個行程在其執行的程序中可以產生多個執行緒,多個執行緒共享行程的堆和方法區記憶體資源,每個執行緒都有自己的程式計數器、虛擬機堆疊和本地方法堆疊,由于執行緒共享行程的記憶體,因此系統產生一個執行緒或者在多個執行緒之間切換作業時的負擔比行程小得多,執行緒也稱為輕量級行程,
-
行程和執行緒最大的區別是,各行程是獨立的,而各執行緒則不一定獨立,因為同一行程中的多個執行緒極有可能會相互影響,執行緒執行開銷小,但不利于資源的管理和保護,行程則相反
5. 什么是守護執行緒?
Java執行緒分為用戶執行緒和守護執行緒,
- 守護執行緒是程式運行的時候在后臺提供一種通用服務的執行緒,所有用戶執行緒停止,行程會停掉所有守護執行緒,退出程式,
- Java中把執行緒設定為守護執行緒的方法:在 start 執行緒之前呼叫執行緒的 setDaemon(true) 方法,
6. java的執行緒大概有幾種狀態?
執行緒在運行的生命周期中的任何時刻只能是 6 種不同狀態的其中一種,
- 初始狀態(NEW):執行緒已經構建,尚未啟動,
- 運行狀態(RUNNABLE):包括就緒(READY)和運行中(RUNNING)兩種狀態,統稱為運行狀態,
- 阻塞狀態(BLOCKED):執行緒被鎖阻塞,
- 等待狀態(WAITING):執行緒需要等待其他執行緒做出特定動作(通知或中斷),
- 終止狀態(TERMINATED):當前執行緒已經執行完畢,
7. 說說與執行緒相關的方法
- 加鎖物件的 wait() 方法,使一個執行緒處于等待狀態,并且釋放所持有的物件的鎖
- 加鎖物件的 notify() 方法,由 JVM 喚醒一個處于等待狀態的執行緒,具體哪個執行緒不確定,且與優先級無關
- 加鎖物件的 notityAll() 方法,喚醒所有處入等待狀態的執行緒,讓它們重新競爭物件的鎖
- 執行緒的 sleep() 方法,使一個正在運行的執行緒處于睡眠狀態,是靜態方法,呼叫此方法要捕捉 InterruptedException 例外
- JDK 1.5 開始通過 Lock 介面提供了顯式鎖機制,豐富了鎖的功能,可以嘗試加鎖和加鎖超時,Lock 介面中定義了加鎖 lock()、釋放鎖 unlock() 方法 和 newCondition() 產生用于執行緒之間通信的 Condition 物件的方法
- JDK 1.5 開始提供了信號量 Semaphore 機制,信號量可以用來限制對某個共享資源進行訪問的執行緒的數量,在對資源進行訪問之前,執行緒必須呼叫 Semaphore 物件的 acquire() 方法得到信號量的許可;在完成對資源的訪問后,執行緒必須呼叫 Semaphore 物件的 release() 方法向信號量歸還許可
8. sleep 和 wait方法的區別?
- sleep是執行緒類(Thread)的方法,導致此執行緒暫停執行指定時間,把執行機會給其他執行緒,但是監控狀態依然保持,到時后會自動恢復,呼叫sleep不會釋放物件鎖,
- wait是Object類的方法,對此物件呼叫wait方法導致本執行緒放棄物件鎖,進入等待此物件的等待鎖定池,只有針對此物件發出notify方法(或notifyAll)后本執行緒才進入物件鎖定池準備獲得物件鎖進入運行狀態,
9. 執行緒的 run() 和 start() 有什么區別?
-
start方法用于啟動執行緒,真正實作了多執行緒運行,在呼叫T執行緒的sturt方法后.執行緒會在后臺執行,無須等待run方法體的代碼執行完畢,就可以繼續執行下面的代碼,
-
在通過呼叫Thrend 類的start方法啟動一個執行緒時,此執行緒處于就緒狀態,并沒有運行,
-
run方法也叫作執行緒體,包含了要執行的執行緒的邏輯代碼,在呼叫run 方法后.執行緒會進人運行狀態,開始運行run方法中的代碼,在run 方法運行結束后,該執行緒終止,CPU再次調度其他執行緒,
10. sleep()和yield()有什么區別?
- sleep() 方法給其他執行緒運行機會時不考慮執行緒的優先級;yield() 方法只會給相同優先級或更高優先級的執行緒運行的機會
- 執行緒執行 sleep() 方法后進入超時等待狀態;執行緒執行 yield() 方法轉入就緒狀態,可能馬上又得得到執行
- sleep() 方法宣告拋出 InterruptedException;yield() 方法沒有宣告拋出例外
- sleep() 方法需要指定時間引數;yield() 方法出讓 CPU 的執行權時間由 JVM 控制
11. 死鎖的四個條件?
- 互斥條件:一個鎖一次只能由一個行程占有
- 不可剝奪條件:一個行程占有的資源在使用完之前不可以被其他行程剝奪,只能由該行程釋放之后才能被其他行程獲取,
- 請求和保持條件:一個行程在申請資源的同時保持已經占有的資源不釋放,
- 回圈等待條件:同時需要A、B兩個資源的行程分別占有了A和B,形成了兩個行程都阻塞并等待對方釋放資源的狀態,
12. 怎么在開發中避免死鎖?
避免死鎖:
對于以上 4 個條件,只要破壞其中一個條件,就可以避免死鎖的發生,
對于第一個條件 “互斥” 是不能破壞的,因為加鎖就是為了保證互斥,
其他三個條件,我們可以嘗試
- 一次性申請所有的資源,破壞 “占有且等待” 條件
- 占有部分資源的執行緒進一步申請其他資源時,如果申請不到,主動釋放它占有的資源,破壞 “不可搶占” 條件
- 按序申請資源,破壞 “回圈等待” 條件
編程中的最佳實踐:
使用 Lock 的 tryLock(long timeout, TimeUnit unit)的方法,設定超時時間,超時可以退出防止死鎖
盡量使用并發工具類代替加鎖
盡量降低鎖的使用粒度
盡量減少同步的代碼塊
13. 怎么檢測死鎖?
jstack -l可以查看堆疊運行的狀態,-l會顯示鎖狀態,里面會報告死鎖,
14. 怎么解決死鎖?
1、系統重啟
2、撤銷代價比較低的執行緒,例如低優先級的執行緒
15.執行緒安全是什么?如何保證執行緒安全?
當多個執行緒訪問某個方法時,不管你通過怎樣的呼叫方式或者說這些執行緒如何交替的執行,我們在主程式中不需要去做任何的同步,這個類的結果行為都是我們設想的正確行為,那么我們就可以說這個類是執行緒安全的,
synchronized關鍵字,就是用來控制執行緒同步的,保證我們的執行緒在多執行緒環境下,不被多個執行緒同時執行,確保我們資料的完整性,使用方法一般是加在方法上,
就是我們在需要的時候去手動的獲取鎖和釋放鎖,甚至我們還可以中斷獲取以及超時獲取的同步特性,但是從使用上說Lock明顯沒有synchronized使用起來方便快捷,
16. 10個執行緒,一個執行緒出錯,怎么通知其它的執行緒,
重寫了自定義執行緒組的uncaughtException()方法后,加上相應的中斷操作和判斷,是可以做到當某個執行緒出現例外然后中斷時,其他的執行緒也會馬上運行結束,不過這里的其他執行緒指得是當前和出現例外的執行緒在同一執行緒組的執行緒們,而在例外執行緒之后新加入執行緒組的執行緒就不會被影響到的,從正常執行緒可以持續運行下去就可以證明這點,所以即使采取了例外中斷的手段,但是當執行緒組內的某個執行緒出現例外,只會影響到當前在執行緒組內的執行緒的運行情況,例外之后才加入到執行緒組的執行緒就不會被停止了,
17. 如何避免指令重排序
觀察加入volatile關鍵字和沒有加入volatile關鍵字時所生成的匯編代碼發現,加入volatile關鍵字時,會多出一個lock前綴指令”
lock前綴指令實際上相當于一個記憶體屏障(也成記憶體柵欄),記憶體屏障會提供3個功能:
1)它確保指令重排序時不會把其后面的指令排到記憶體屏障之前的位置,也不會把前面的指令排到記憶體屏障的后面;即在執行到記憶體屏障這句指令時,在它前面的操作已經全部完成;
2)它會強制將對快取的修改操作立即寫入主存;
3)如果是寫操作,它會導致其他CPU中對應的快取行無效,
18. volatile除了避免指令重排序還有什么功能
Java提供了volatile關鍵字來保證可見性,
當一個共享變數被volatile修飾時,它會保證修改的值會立即被更新到主存,當有其他執行緒需要讀取時,它會去記憶體中讀取新值,
從實踐角度而言,volatile的一個重要作用就是和CAS結合,保證了原子性,詳細的可以參見java.util.concurrent.atomic包下的類,比如AtomicInteger,
19. 說說volatile關鍵字
對于可見性,Java提供了volatile關鍵字來保證可見性,
當一個共享變數被volatile修飾時,它會保證修改的值會立即被更新到主存,當有其他執行緒需要讀取時,它會去記憶體中讀取新值,
從實踐角度而言,volatile的一個重要作用就是和CAS結合,保證了原子性,詳細的可以參見java.util.concurrent.atomic包下的類,比如AtomicInteger,
20. ThreadLocal有什么作用?有哪些使用場景?
ThreadLocal 是執行緒本地存盤,在每個執行緒中都創建了一個 ThreadLocalMap 物件,每個執行緒可以訪問自己內部 ThreadLocalMap 物件內的 value,通過這種方式,避免資源在多執行緒間共享,
經典的使用場景是為每個執行緒分配一個 JDBC 連接 Connection,這樣就可以保證每個執行緒的都在各自的 Connection 上進行資料庫的操作,不會出現 A 執行緒關了 B執行緒正在使用的 Connection; 還有 Session 管理 等問題,
21. 高并發下,如何安全地修改同一行資料?
- 可以將資料加載到快取中,利用 CAS 方式進行更新
- 也可以將所有請求放到同一個訊息佇列里,異步回傳,按順序執行更新
注意:
- 如果使用悲觀鎖,在并發請求量很大的情況下,會導致服務和資料連接數耗盡,系統卡死
22. synchronized 和 volatile 的區別是什么?
作用:
- synchronized 表示只有一個執行緒可以獲取作用物件的鎖,執行代碼,阻塞其他執行緒,
- volatile 表示變數在 CPU 的暫存器中是不確定的,必須從主存中讀取,保證多執行緒環境下變數的可見性;禁止指令重排序,
區別:
- synchronized 可以作用于變數、方法、物件;volatile 只能作用于變數,
- synchronized 可以保證執行緒間的有序性(個人猜測是無法保證執行緒內的有序性,即執行緒內的代碼可能被 CPU 指令重排序)、原子性和可見性;volatile 只保證了可見性和有序性,無法保證原子性,
- synchronized 執行緒阻塞,volatile 執行緒不阻塞,
- volatile 本質是告訴 jvm 當前變數在暫存器中的值是不安全的需要從記憶體中讀取;sychronized 則是鎖定當前變數,只有當前執行緒可以訪問到該變數其他執行緒被阻塞,
- volatile 標記的變數不會被編譯器優化;synchronized 標記的變數可以被編譯器優化,
23. synchronized 和 Lock 有什么區別?
- 實作層面不一樣,synchronized 是 Java 關鍵字,JVM層面 實作加鎖和釋放鎖;Lock 是一個介面,在代碼層面實作加鎖和釋放鎖
- 是否自動釋放鎖,synchronized 在執行緒代碼執行完或出現例外時自動釋放鎖;Lock 不會自動釋放鎖,需要再 finally {} 代碼塊顯式地中釋放鎖
- 是否一直等待,synchronized 會導致執行緒拿不到鎖一直等待;Lock 可以設定嘗試獲取鎖或者獲取鎖失敗一定時間超時
- 獲取鎖成功是否可知,synchronized 無法得知是否獲取鎖成功;Lock 可以通過 tryLock 獲得加鎖是否成功
- 功能復雜性,synchronized 加鎖可重入、不可中斷、非公平;Lock 可重入、可判斷、可公平和不公平、細分讀寫鎖提高效率
24. synchronized 和 ReentrantLock 區別是什么?
- synchronized 競爭鎖時會一直等待;ReentrantLock 可以嘗試獲取鎖,并得到獲取結果
- synchronized 獲取鎖無法設定超時;ReentrantLock 可以設定獲取鎖的超時時間
- synchronized 無法實作公平鎖;ReentrantLock 可以滿足公平鎖,即先等待先獲取到鎖
- synchronized 控制等待和喚醒需要結合加鎖物件的 wait() 和 notify()、notifyAll();ReentrantLock 控制等待和喚醒需要結合 Condition 的 await() 和 signal()、signalAll() 方法
- synchronized 是 JVM 層面實作的;ReentrantLock 是 JDK 代碼層面實作
- synchronized 在加鎖代碼塊執行完或者出現例外,自動釋放鎖;ReentrantLock 不會自動釋放鎖,需要在 finally{} 代碼塊顯示釋放
補充一個相同點:都可以做到同一執行緒,同一把鎖,可重入代碼塊,
四、計算機網路
1. get 和 post的區別
- Get是不安全的,因為在傳輸程序,資料被放在請求的URL中;Post的所有操作對用戶來說都是不可見的, 但是這種做法也不時絕對的,大部分人的做法也是按照上面的說法來的,但是也可以在get請求加上 request body,給 post請求帶上 URL 引數,
- Get請求提交的url中的資料最多只能是2048位元組,這個限制是瀏覽器或者服務器給添加的,http協議并沒有對url長度進行限制,目的是為了保證服務器和瀏覽器能夠正常運行,防止有人惡意發送請求,Post請求則沒有大小限制,
- Get限制Form表單的資料集的值必須為ASCII字符;而Post支持整個ISO10646字符集,
- Get執行效率卻比Post方法好,Get是form提交的默認方法,
- GET產生一個TCP資料包;POST產生兩個TCP資料包,
- 對于GET方式的請求,瀏覽器會把http header和data一并發送出去,服務器回應200(回傳資料);
- 而對于POST,瀏覽器先發送header,服務器回應100 continue,瀏覽器再發送data,服務器回應200 ok(回傳資料),
2. TCP和UDP的區別?tcp擁塞控制和流量控制如何實作?
TCP,Transmission Control Protocol 的縮寫,即傳輸控制協議,
- 面向連接,即必須在雙方建立可靠連接之后,才會收發資料
- 資訊包頭 20 個位元組
- 建立可靠連接需要經過3次握手
- 斷開連接需要經過4次揮手
- 需要維護連接狀態
- 報文頭里面的確認序號、累計確認及超時重傳機制能保證不丟包、不重復、按序到達
- 擁有流量控制及擁塞控制的機制
UDP,User Data Protocol 的縮寫,即用戶資料報協議,
- 不建立可靠連接,無需維護連接狀態
- 資訊包頭 8 個位元組
- 接收端,UDP 把訊息段放在佇列中,應用程式從佇列讀訊息
- 不受擁擠控制演算法的調節
- 傳送資料的速度受應用軟體生成資料的速率、傳輸帶寬、源端和終端主機性能的限制
- 面向資料報,不保證接收端一定能收到
流量控制
TCP 利用滑動視窗實作流量控制,
流量控制是為了控制發送方發送速率,保證接收方來得及接收,
接收方發送的確認報文中的視窗欄位可以用來控制發送方視窗大小,從而影響發送方的發送速率,將視窗欄位設定為 0,則發送方不能發送資料,
擁塞控制
為了進行擁塞控制,TCP 發送方要維持一個 擁塞視窗(cwnd) 的狀態變數,擁塞控制視窗的大小取決于網路的擁塞程度,并且動態變化,發送方讓自己的發送視窗取為擁塞視窗和接收方的接受視窗中較小的一個,
TCP的擁塞控制采用了四種演算法,即 慢開始 、 擁塞避免 、快重傳 和 快恢復,在網路層也可以使路由器采用適當的分組丟棄策略(如主動佇列管理 AQM),以減少網路擁塞的發生,
3. 輸入一次url程序,用到哪些協議?

-
DNS 決議:瀏覽器查詢 DNS,獲取域名對應的 IP 地址:具體程序包括瀏覽器搜索自身的 DNS 快取、搜索作業系統的 DNS 快取、讀取本地的 Host 檔案和向本地 DNS 服務器進行查詢等,對于向本地 DNS 服務器進行查詢,如果要查詢的域名包含在本地配置區域資源中,則回傳決議結果給客戶機,完成域名決議(此決議具有權威性);如果要查詢的域名不由本地 DNS 服務器區域決議,但該服務器已快取了此網址映射關系,則呼叫這個 IP 地址映射,完成域名決議(此決議不具有權威性),如果本地域名服務器并未快取該網址映射關系,那么將根據其設定發起遞回查詢或者迭代查詢;
-
TCP 連接:瀏覽器獲得域名對應的 IP 地址以后,瀏覽器向服務器請求建立鏈接,發起三次握手;
-
發送 HTTP 請求:TCP 連接建立起來后,瀏覽器向服務器發送 HTTP 請求;
-
服務器處理請求并回傳 HTTP 報文:服務器接收到這個請求,并根據路徑引數映射到特定的請求處理器進行處理,并將處理結果及相應的視圖回傳給瀏覽器;
-
瀏覽器決議渲染頁面:瀏覽器決議并渲染視圖,若遇到對 js 檔案、css 檔案及圖片等靜態資源的參考,則重復上述步驟并向服務器請求這些資源;瀏覽器根據其請求到的資源、資料渲染頁面,最終向用戶呈現一個完整的頁面,
-
連接結束,
5. HTTP和HTTPS的區別,
安全性上,HTTPS是安全超文本協議,在HTTP基礎上有更強的安全性,簡單來說,HTTPS是使用TLS/SSL加密的HTTP協議
申請證書上,HTTPS需要使用ca申請證書
傳輸協議上, HTTP是超文本傳輸協議,明文傳輸;HTTPS是具有安全性的 SSL 加密傳輸協議
連接方式與埠上,http的連接簡單,是無狀態的,埠是 80; https 在http的基礎上使用了ssl協議進行加密傳輸,埠是 443
6. HTTPS加密認證程序,
(1)發起請求:客戶端在通過TCP和服務器建立連接之后(默認使用443埠),發出一個請求證書的訊息給服務器,在該請求訊息里包含自己可實作的演算法串列和其他需要的訊息,
(2)證書回傳:服務器端在收到訊息后回應客戶端并回傳證書,在證書中包含服務器資訊、域名、申請證書的公司、公鑰、資料加密演算法等,
(3)證書驗證:客戶端在收到證書后,判斷證書簽發機構是否正確,并使用該簽發機構的公鑰確認簽名是否有效,客戶端還會確保在證書中列出的域名為正在連接的域名,如果客戶端確認證書有效,則生成對稱密鑰,并使用公鑰將對稱密鑰加密,
(4)密鑰交換:客戶端將加密后的對稱密鑰發送給服務器,服務器在接收到對稱密鑰后使用私鑰解密,
(5)資料傳輸:經過上述步驟,客戶端和服務器就完成了密鑰對的交換,在之后的資料傳輸程序中,客戶端和服務端就可以基于對稱加密(加密和解密使用相同密鑰的加密演算法)將資料加密后在網路上傳輸,保證了網路資料傳輸的安全性,
7. TCP的三次握手,四次揮手
客戶端–發送帶有 SYN 標志的資料包–一次握手–服務端
服務端–發送帶有 SYN/ACK 標志的資料包–二次握手–客戶端
客戶端–發送帶有帶有 ACK 標志的資料包–三次握手–服務端
客戶端-發送一個 FIN,用來關閉客戶端到服務器的資料傳送
服務器-收到這個 FIN,它發回一 個 ACK,確認序號為收到的序號加 1 ,和 SYN 一樣,一個 FIN 將占用一個序號
服務器-關閉與客戶端的連接,發送一個 FIN 給客戶端
客戶端-發回 ACK 報文確認,并將確認序號設定為收到序號加 1
五、JVM
1. GC標記方法,
參考計數和可達性分析,
- 參考計數實作起來比較簡單,就是給物件添加一個參考計數器,每當有一個地方參考它時就加1,參考失效時就減1,當計數器為0的時候就標記為可回收,
- 可達性分析的基本思路就是:通過將一些稱為”GC Roots”的物件作為起始點,從這些節點開始搜索,搜索和該節點發生直接或者間接參考關系的物件,將這些物件以鏈的形式組合起來,形成一張“關系網”,又叫做參考鏈,最后垃圾收集器就回收那些不在這張關系網上的物件,
2. finalize方法,
Java提供finalize()方法,垃圾回收器準備釋放記憶體的時候,會先呼叫finalize(),
(1).物件不一定會被回收,
(2).垃圾回收不是解構式,
(3).垃圾回收只與記憶體有關,
(4).垃圾回收和finalize()都是靠不住的,只要JVM還沒有快到耗盡記憶體的地步,它是不會浪費時間進行垃圾回收的,
3. 說一下垃圾回識訓制?什么時候垃圾回收?
垃圾回識訓制,簡稱 GC
- Java 語言不需要程式員直接控制記憶體回收,由 JVM 在后臺自動回收不再使用的記憶體
- 提高編程效率
- 保護程式的完整性
- JVM 需要跟蹤程式中有用的物件,確定哪些是無用的,影響性能
特點
- 回收 JVM 堆記憶體里的物件空間,不負責回收堆疊記憶體資料
- 無法處理一些作業系統資源的釋放,如資料庫連接、輸入流輸出流、Socket 連接
- 垃圾回收發生具有不可預知性,程式無法精確控制垃圾回識訓制執行
- 可以將物件的參考變數設定為 null,垃圾回識訓制可以在下次執行時回收該物件,
- JVM 有多種垃圾回收 實作演算法,表現各異
- 垃圾回識訓制回收任何物件之前,會先呼叫物件的 finalize() 方法
- 可以通過 System.gc() 或 Runtime.getRuntime().gc() 通知系統進行垃圾回收,會有一些效果,但系統是否進行垃圾回收依然不確定
- 不要主動呼叫物件的 finalize() 方法,應該交給垃圾回識訓制呼叫
4. 堆記憶體和堆疊記憶體有什么區別?堆和堆疊哪個快?什么變數存在堆疊里面?
堆疊記憶體和堆記憶體都是存盤資料的地方,
堆疊記憶體中存盤的值的大小是固定的,堆記憶體中存盤值的大小不固定的,
堆疊:由系統自動分配,速度較快,但程式員是無法控制的,
堆:是由new分配的記憶體,一般速度比較慢,而且容易產生記憶體碎片,不過用起來最方便,
堆疊: 在函式呼叫時,第一個進堆疊的是主函式中函式呼叫后的下一條指令(函式呼叫陳述句的下一條可執行陳述句)的地址,然后是函式的各個引數,在大多數的C編譯器中,引數是由右往左入堆疊的,然后是函式中的區域變數,注意靜態變數是不入堆疊的,
當本次函式呼叫結束后,區域變數先出堆疊,然后是引數,最后堆疊頂指標指向最開始存的地址,也就是主函式中的下一條指令,程式由該點繼續運行,
堆:一般是在堆的頭部用一個位元組存放堆的大小,堆中的具體內容有程式員安排,
5. Java中類加載程序是什么樣的?
類加載的步驟為,加載 -> 驗證 -> 準備 -> 決議 -> 初始化,
1、加載:
獲取類的二進制位元組流
將位元組流代表的靜態存盤結構轉化為方法區運行時資料結構
在堆中生成class位元組碼物件
2、驗證:連接程序的第一步,確保 class 檔案的位元組流中的資訊符合當前 JVM 的要求,不會危害 JVM 的安全
3、準備:為類的靜態變數分配記憶體并將其初始化為默認值
4、決議:JVM 將常量池內符號參考替換成直接參考的程序
5、初始化:執行類構造器的初始化的程序
6. JVM 如何確定垃圾物件:
JVM 采用的是可達性分析演算法,通過 GC Roots 來判定物件是否存活,從 GC Roots 向下追溯、搜索,會產生 Reference Chain,當一個物件不能和任何一個 GC Root 產生關系時,就判定為垃圾,
軟參考和弱參考,也會影響物件的回收,記憶體不足時會回收軟參考物件;GC 時會回收弱參考物件,
7. 回收演算法
判斷物件是否可回收的演算法有兩種:
-
Reference Counting GC,參考計數演算法
-
Tracing GC,可達性分析演算法
JVM 各廠商基本都是用的 Tracing GC 實作
大部分垃圾收集器遵從了分代收集(Generational Collection)理論,
針對新生代與老年代回收垃圾記憶體的特點,提出了 3 種不同的演算法:
1、標記-清除演算法(Mark-Sweep)
標記需回收物件,統一回收;或標記存活物件,回收未標記物件,
缺點:
大量物件需要標記與清除時,效率不高
標記、清除產生的大量不連續記憶體碎片,導致無法分配大物件
2、標記-復制演算法(Mark-Copy)
可用記憶體等分兩塊,使用其中一塊 A,用完將存活的物件復制到另外一塊 B,一次性清空 A,然后改分配新物件到 B,如此回圈,
缺點:
不適合大量物件不可回收的情況,換句話說就是僅適合大量物件可回收,少量物件需復制的區域
只能使用記憶體容量的一半,浪費較多記憶體空間
3、標記-整理演算法(Mark-Compact)
標記存活的物件,統一移到記憶體區域的一邊,清空占用記憶體邊界以外的記憶體,
缺點:
移動大量存活物件并更新參考,需暫停程式運行
8. 說說JVM記憶體區域分為幾大塊,分別講一下
Java 虛擬機在執行 Java 程式的程序中會把他所管理的記憶體劃分為若干個不同的資料區域:
- 程式計數器:可以看作是當前執行緒所執行的位元組碼檔案(class)的行號指示器,它會記錄執行痕跡,是每個執行緒私有的
- 方法區:主要存盤已被虛擬機加載的類的資訊、常量、靜態變數和即時編譯器編譯后的代碼等資料,該區域是被執行緒共享的,很少發生垃圾回收
- 堆疊:堆疊是運行時創建的,是執行緒私有的,生命周期與執行緒相同,存盤宣告的變數
- 本地方法堆疊:為 native 方法服務,native 方法是一種由非 java 語言實作的 java 方法,與 java 環境外互動,如可以用本地方法與作業系統互動
- 堆:堆是所有執行緒共享的一塊記憶體,是在 java 虛擬機啟動時創建的,幾乎所有物件實體都在此創建,所以經常發生垃圾回收操作
JDK8 之前,Hotspot 中方法區的實作是永久代(Perm)
JDK8 開始使用元空間(Metaspace),以前永久代所有內容的字串常量移至堆記憶體,其他內容移至元空間,元空間直接在本地記憶體分配,
六、資料庫
1. 說說mysql的存盤引擎
InnoDB
- 默認事務型引擎,被廣泛使用的存盤引擎
- 資料存盤在共享表空間,即多個表和索引都存盤在一個表空間中,可通過組態檔修改
- 主鍵查詢的性能高于其他型別的存盤引擎
- 內部做了很多優化,如:從磁盤讀取資料時會自動構建hash索引,插入資料時自動構建插入緩沖區
- 通過一些機制和工具支持真正的熱備份
- 支持崩潰后的安全恢復
- 支持行級鎖
- 支持外鍵
MyISAM
- 擁有全文索引、壓縮、空間函式
- 不支持事務和行級鎖、不支持崩潰后的安全恢復
- 表存盤在兩個檔案:MYD 和 MYI
- 設計簡單,某些場景下性能很好,例如獲取整個表有多少條資料,性能很高
2. 講下索引以及應用場景
- 當我們使用order by將查詢結果按照某個欄位排序時,如果該欄位沒有建立索引,那么執行計劃會將查詢出的所有資料使用外部排序,這個操作是很影響性能的,但是如果我們對該欄位建立索引,那么由于索引本身是有序的,因此直接按照索引的順序和映射關系逐條取出資料即可,
- 對join陳述句匹配關系(on)涉及的欄位建立索引能夠提高效率,
- 查找符合where條件的記錄時
- 如果要查詢的欄位都建立過索引,那么引擎會直接在索引表中查詢而不會訪問原始資料(否則只要有一個欄位沒有建立索引就會做全表掃描),這叫索引覆寫,因此我們需要盡可能的在select后只寫必要的查詢欄位,以增加索引覆寫的幾率,
3. 索引的作用?索引有什么缺點?
索引是一種用于快速查詢和檢索資料的資料結構,常見的索引結構有: B 樹, B+樹和 Hash,
索引的作用就相當于目錄的作用,打個比方: 我們在查字典的時候,如果沒有目錄,那我們就只能一頁一頁的去找我們需要查的那個字,速度很慢,如果有目錄了,我們只需要先去目錄里查找字的位置,然后直接翻到那一頁就行了,
優點 :
- 使用索引可以大大加快 資料的檢索速度(大大減少檢索的資料量), 這也是創建索引的最主要的原因,
- 通過創建唯一性索引,可以保證資料庫表中每一行資料的唯一性,
缺點 :
- 創建索引和維護索引需要耗費許多時間,當對表中的資料進行增刪改的時候,如果資料有索引,那么索引也需要動態的修改,會降低 SQL 執行效率,
- 索引需要使用物理檔案存盤,也會耗費一定空間,
4. 創建索引的原則
索引雖好,但也不是無限制的使用,最好符合一下幾個原則
1) 最左前綴匹配原則,組合索引非常重要的原則,mysql會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整,
2)較頻繁作為查詢條件的欄位才去創建索引
3)更新頻繁欄位不適合創建索引
4)若是不能有效區分資料的列不適合做索引列(如性別,男女未知,最多也就三種,區分度實在太低)
5)盡量的擴展索引,不要新建索引,比如表中已經有a的索引,現在要加(a,b)的索引,那么只需要修改原來的索引即可,
6)定義有外鍵的資料列一定要建立索引,
7)對于那些查詢中很少涉及的列,重復值比較多的列不要建立索引,
8)對于定義為text、image和bit的資料型別的列不要建立索引,
5. 為什么MySQL 沒有使用Hash作為索引的資料結構呢?
1.Hash 沖突問題 :我們上面也提到過Hash 沖突了,不過對于資料庫來說這還不算最大的缺點,
2.Hash 索引不支持順序和范圍查詢(Hash 索引不支持順序和范圍查詢是它最大的缺點: 假如我們要對表中的資料進行排序或者進行范圍查詢,那 Hash 索引可就不行了,
6. MySQL索引使用的什么資料結構,B樹和B+樹的區別
目前大部分資料庫系統及檔案系統都采用 B-Tree 或其變種 B+Tree 作為索引結構,
在B樹中,你可以將鍵和值存放在內部節點和葉子節點;但在B+樹中,內部節點都是鍵,沒有值,葉子節點同時存放鍵和值,
B+樹的葉子節點有一條鏈相連,而B樹的葉子節點各自獨立,
B樹可以在內部節點同時存盤鍵和值,因此,把頻繁訪問的資料放在靠近根節點的地方將會大大提高熱點資料的查詢效率,這種特性使得B樹在特定資料重復多次查詢的場景中更加高效,
由于B+樹的內部節點只存放鍵,不存放值,因此,一次讀取,可以在記憶體頁中獲取更多的鍵,有利于更快地縮小查找范圍, B+樹的葉節點由一條鏈相連,因此,當需要進行一次全資料遍歷的時候,B+樹只需要使用O(logN)時間找到最小的一個節點,然后通過鏈進行O(N)的順序遍歷即可,而B樹則需要對樹的每一層進行遍歷,這會需要更多的記憶體置換次數,因此也就需要花費更多的時間
7. 索引為什么采用B+樹的資料結構,而不使用二叉樹或者紅黑樹
Hash的存盤結構是key-value形式存在陣列中,對資料進行Hash(散列)運算,然后將哈希結果作為檔案指標,可以從索引檔案中獲得資料的檔案指標,再到資料檔案中獲取到資料,查詢效率非常高,主流的Hash演算法有MD5、SHA256等等,無法解決范圍查詢的場景,比如 select count(id) from sus_user where id >10;因此Hash這種索引結構只能針對欄位名=目標值的場景使用,不適合模糊查詢的場景,
紅黑樹也叫平衡二叉樹,它不僅繼承了二叉樹的優點,而且解決了上面二叉樹遇到的自增整形索引的問題,而且紅黑樹會左旋、右旋對結構進行調整,始終保證左子節點數 < 父節點數 < 右子節點數的規則,但在資料量大的時候,深度也很大,如果我們有很多資料,那么樹的深度依然會很大,可能就會超過十幾二十層以上,對我們的磁盤尋址不利,依然會花費很多時間查找,
B+樹存盤結構,只有葉子節點存盤資料,B+樹結構沒有在所有的節點里存盤記錄資料,而是只在最下層的葉子節點存盤,上層的所有非葉子節點只存放索引資訊,這樣的結構可以讓單個節點存放下更多索引值,提高命中目標記錄的幾率,這種結構會在上層非葉子節點存盤一部分冗余資料,但是這樣的缺點都是可以容忍的,因為冗余的都是索引資料,不會對記憶體造成大的負擔,
8. 聚簇索引和非聚簇索引這兩個概念怎么理解?
聚集索引
聚集索引即索引結構和資料一起存放的索引,主鍵索引屬于聚集索引,
在 Mysql 中,InnoDB 引擎的表的 .ibd檔案就包含了該表的索引和資料,對于 InnoDB 引擎表來說,該表的索引(B+樹)的每個非葉子節點存盤索引,葉子節點存盤索引和索引對應的資料,
聚集索引的優點
聚集索引的查詢速度非常的快,因為整個 B+樹本身就是一顆多叉平衡樹,葉子節點也都是有序的,定位到索引的節點,就相當于定位到了資料,
聚集索引的缺點
依賴于有序的資料 :因為 B+樹是多路平衡樹,如果索引的資料不是有序的,那么就需要在插入時排序,如果資料是整型還好,否則類似于字串或 UUID 這種又長又難比較的資料,插入或查找的速度肯定比較慢,
更新代價大 : 如果對索引列的資料被修改時,那么對應的索引也將會被修改, 而且況聚集索引的葉子節點還存放著資料,修改代價肯定是較大的, 所以對于主鍵索引來說,主鍵一般都是不可被修改的,
非聚集索引
非聚集索引即索引結構和資料分開存放的索引,
二級索引屬于非聚集索引,
MYISAM 引擎的表的.MYI 檔案包含了表的索引, 該表的索引(B+樹)的每個葉子非葉子節點存盤索引, 葉子節點存盤索引和索引對應資料的指標,指向.MYD 檔案的資料,
非聚集索引的葉子節點并不一定存放資料的指標, 因為二級索引的葉子節點就存放的是主鍵,根據主鍵再回表查資料,
非聚集索引的優點
更新代價比聚集索引要小 ,非聚集索引的更新代價就沒有聚集索引那么大了,非聚集索引的葉子節點是不存放資料的
非聚集索引的缺點
跟聚集索引一樣,非聚集索引也依賴于有序的資料
可能會二次查詢(回表) :這應該是非聚集索引最大的缺點了, 當查到索引對應的指標或主鍵后,可能還需要根據指標或主鍵再到資料檔案或表中查詢,
9. mysql索引優化相關方法,聯合索引應該把什么欄位放在第一個位置?
索引優化的方法有以下,第一:欄位選擇性,查詢條件含有多個欄位時,不要在選擇性很低欄位上創建索引,可通過創建組合索引來增強低欄位選擇性和避免選擇性很低欄位創建索引帶來副作用,正確索引會提高sql查詢速度,過多索引會增加優化器選擇索引的代價,不要濫用索引;第二:Explain優化查詢檢測,EXPLAIN可以幫助開發人員分析SQL問題,explain顯示了mysql如何使用索引來處理select陳述句以及連接表,可以幫助選擇更好的索引和寫出更優化的查詢陳述句,
在建立聯合索引的時候應該注意索引列的順序,一般情況下,將查詢需求頻繁或者欄位選擇性高的列放在前面,
10. mysql的最左原則嗎?
- 顧名思義,就是最左優先,在創建多列索引時,要根據業務需求,where子句中使用最頻繁的一列放在最左邊,
- 最左前綴匹配原則,非常重要的原則,mysql會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整,
- =和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式
11. 臟讀、幻讀、不可重復讀指什么?
- 臟讀:一個事務讀取另外一個事務還沒有提交的資料,
- 不可重復讀:一個事務內,兩次相同條件的查詢回傳了不同的結果,
- 幻讀:同一個事務中,一條資料出現在這次查詢的結果集里,卻沒有出現在之前的查詢結果集中,例如,在一個事務中進行了同一個查詢運行了兩次,期間被另外一個事務提交插入一行或修改查詢條件匹配的一行,它比不可重復讀更難防范,因為鎖定第一個查詢結果集的所有行并不能阻止導致幻象出現的更改,
12. 資料庫事務的四個特性:
事務具備ACID四種特性,ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔離性)和Durability(持久性)的英文縮寫,
-
原子性(Atomicity)
事務最基本的操作單元,要么全部成功,要么全部失敗,不會結束在中間某個環節,事務在執行程序中發生錯誤,會被回滾到事務開始前的狀態,就像這個事務從來沒有執行過一樣, -
一致性(Consistency)
事務的一致性指的是在一個事務執行之前和執行之后資料庫都必須處于一致性狀態,如果事務成功地完成,那么系統中所有變化將正確地應用,系統處于有效狀態,如果在事務中出現錯誤,那么系統中的所有變化將自動地回滾,系統回傳到原始狀態, -
隔離性(Isolation)
指的是在并發環境中,當不同的事務同時操縱相同的資料時,每個事務都有各自的完整資料空間,由并發事務所做的修改必須與任何其他并發事務所做的修改隔離,事務查看資料更新時,資料所處的狀態要么是另一事務修改它之前的狀態,要么是另一事務修改它之后的狀態,事務不會查看到中間狀態的資料, -
持久性(Durability)
指的是只要事務成功結束,它對資料庫所做的更新就必須永久保存下來,即使發生系統崩潰,重新啟動資料庫系統后,資料庫還能恢復到事務成功結束時的狀態,
13. 說說sql的事務隔離級別,具體的應用場景
- 讀未提交(Read Uncommitted):是最低的事務隔離級別,它允許另外一個事務可以看到這個事務未提交的資料,會出現臟讀,幻讀,不可重復讀,所有并發問題都可能遇到,
- 讀已提交(Read Committed):保證一個事物提交后才能被另外一個事務讀取,另外一個事務不能讀取該事物未提交的資料,不會出現臟讀現象,但是會出現幻讀,不可重復讀,
- 可重復讀(Repeatable Read):這種事務隔離級別可以防止臟讀,不可重復讀,但是可能會出現幻象讀,它除了保證一個事務不能被另外一個事務讀取未提交的資料之外還避免了不可重復讀,
- 串行化(Serializable):這是花費最高代價但最可靠的事務隔離級別,事務被處理為順序執行,防止臟讀、不可重復讀、幻象讀,
14. 說說資料庫的樂觀鎖和悲觀鎖?
1.樂觀鎖
樂觀鎖在讀資料時,認為別人不會去寫其所讀的資料:悲觀鎖就剛好相反,覺得自己讀資料時,別人可能剛好在寫自己剛讀的資料,態度比較保守;時間戳在操作資料時不加鎖,而是通過時間戳來控制并發出現的問題,
2.悲觀鎖
悲觀鎖指在其修改某條資料時,不允許別人讀取該資料,直到自己的整個事務都提交并釋放鎖,其他用戶才能訪問該資料,悲觀鎖又可分為排它鎖(寫鎖)和共享鎖(讀鎖),
七、設計模式
1. 你最熟悉的設計模式 ?
單例模式
保證一個類只有一個實體,并且提供一個訪問該全域訪問點
2.那些地方用到了單例模式
網站的計數器,一般也是采用單例模式實作,否則難以同步,
應用程式的日志應用,一般都是單例模式實作,只有一個實體去操作才好,否則內容不好追加顯示,
多執行緒的執行緒池的設計一般也是采用單例模式,因為執行緒池要方便對池中的執行緒進行控制
Windows的(任務管理器)就是很典型的單例模式,他不能打開倆個
windows的(回收站)也是典型的單例應用,在整個系統運行程序中,回收站只維護一個實體,
3.單例優缺點
優點:
在單例模式中,活動的單例只有一個實體,對單例類的所有實體化得到的都是相同的一個實體,這樣就防止其它物件對自己的實體化,確保所有的物件都訪問一個實體
單例模式具有一定的伸縮性,類自己來控制實體化行程,類就在改變實體化行程上有相應的伸縮性,
提供了對唯一實體的受控訪問,
由于在系統記憶體中只存在一個物件,因此可以節約系統資源,當需要頻繁創建和銷毀的物件時單例模式無疑可以提高系統的性能,
允許可變數目的實體,
避免對共享資源的多重占用,
缺點:
不適用于變化的物件,如果同一型別的物件總是要在不同的用例場景發生變化,單例就會引起資料的錯誤,不能保存彼此的狀態,
由于單利模式中沒有抽象層,因此單例類的擴展有很大的困難,
單例類的職責過重,在一定程度上違背了“單一職責原則”,
濫用單例將帶來一些負面問題,如為了節省資源將資料庫連接池物件設計為的單例類,可能會導致共享連接池物件的程式過多而出現連接池溢位;如果實體化的物件長時間不被利用,系統會認為是垃圾而被回收,這將導致物件狀態的丟失,
4.單例模式使用注意事項:
使用時不能用反射模式創建單例,否則會實體化一個新的物件
使用懶單例模式時注意執行緒安全問題
餓單例模式和懶單例模式構造方法都是私有的,因而是不能被繼承的,有些單例模式可以被繼承(如登記式模式)
5.單例創建方式
(主要使用懶漢和懶漢式)
餓漢式:類初始化時,會立即加載該物件,執行緒天生安全,呼叫效率高,
懶漢式: 類初始化時,不會初始化該物件,真正需要使用的時候才會創建該物件,具備懶加載功能,
靜態內部方式:結合了懶漢式和餓漢式各自的優點,真正需要物件的時候才會加載,加載類是執行緒安全的,
列舉單例: 使用列舉實作單例模式 優點:實作簡單、呼叫效率高,列舉本身就是單例,由jvm從根本上提供保障!避免通過反射和反序列化的漏洞, 缺點沒有延遲加載,
雙重檢測鎖方式 (因為JVM本質重排序的原因,可能會初始化多次,不推薦使用)
2. 懶漢式你會怎么寫,懶漢式實體化在哪,建構式的權限?
1.餓漢式
餓漢式:類初始化時,會立即加載該物件,執行緒天生安全,呼叫效率高,
package com.lijie;
//餓漢式
public class Demo1 {
// 類初始化時,會立即加載該物件,執行緒安全,呼叫效率高
private static Demo1 demo1 = new Demo1();
private Demo1() {
System.out.println("私有Demo1構造引數初始化");
}
public static Demo1 getInstance() {
return demo1;
}
public static void main(String[] args) {
Demo1 s1 = Demo1.getInstance();
Demo1 s2 = Demo1.getInstance();
System.out.println(s1 == s2);
}
}
2.懶漢式
懶漢式: 類初始化時,不會初始化該物件,真正需要使用的時候才會創建該物件,具備懶加載功能,
package com.lijie;
//懶漢式
public class Demo2 {
//類初始化時,不會初始化該物件,真正需要使用的時候才會創建該物件,
private static Demo2 demo2;
private Demo2() {
System.out.println("私有Demo2構造引數初始化");
}
public synchronized static Demo2 getInstance() {
if (demo2 == null) {
demo2 = new Demo2();
}
return demo2;
}
public static void main(String[] args) {
Demo2 s1 = Demo2.getInstance();
Demo2 s2 = Demo2.getInstance();
System.out.println(s1 == s2);
}
}
3. 單例模式的餓漢式和懶漢式及區別
餓漢式:類初始化時,會立即加載該物件,執行緒天生安全,呼叫效率高,
懶漢式: 類初始化時,不會初始化該物件,真正需要使用的時候才會創建該物件,具備懶加載功能,
4. 常用的設計模式?
創建型
- 工廠模式與抽象工廠模式 (Factory Pattern)(Abstract Factory Pattern)
- 單例模式 (Singleton Pattern)
- 建造者模式 (Builder Pattern)
- 原型模式 (Prototype Pattern)
結構型
- 配接器模式 (Adapter Pattern)
- 裝飾器模式 (Decorator Pattern)
- 橋接模式 (Bridge Pattern)
- 外觀模式 (Facade Pattern)
- 代理模式 (Proxy Pattern)
- 過濾器模式 (Filter、Criteria Pattern)
- 組合模式 (Composite Pattern)
- 享元模式 (Flyweight Pattern)
行為型
- 責任鏈模式(Chain of Responsibility Pattern)
- 觀察者模式(Observer Pattern)
- 模板模式(Template Pattern)
- 命令模式(Command Pattern)
- 解釋器模式(Interpreter Pattern)
- 迭代器模式(Iterator Pattern)
- 中介者模式(Mediator Pattern)
- 策略模式(Strategy Pattern)
- 狀態模式(State Pattern)
- 備忘錄模式(Memento Pattern)
- 空物件模式(Null Object Pattern)
5. 簡單工廠和抽象工廠有什么區別?
簡單工廠模式
是由一個工廠物件創建產品實體,簡單工廠模式的工廠類一般是使用靜態方法,通過不同的引數的創建不同的物件實體
可以生產結構中的任意產品,不能增加新的產品
抽象工廠模式
提供一個創建一系列相關或相互依賴物件的介面,而無需制定他們具體的類,生產多個系列產品
生產不同產品族的全部產品,不能新增產品,可以新增產品族
6. 說一說設計模式中的代理模式?
代理模式指為物件提供-種通過代理的方式來訪問并控制該物件行為的方法,在客戶端不適合或者不能夠直接參考一-個物件時,可以通過該物件的代理物件來實作對該物件的訪問,可以將該代理物件理解為客戶端和目標物件之間的中介者,
在現實生活也能看到代理模式的身影,比如企業會把五險一金業務交給第三方人力資源公司去做,因為人力資源公司對五險一金 業務更加熟悉,
在代理模式下有兩種角色,一種是被代理者,一種是代理( Proxy),在被代理者需要做一項作業時,不用自己做,而是交給代理做,比如企業在招人時,不用自己去人才市場上找,可以通過代理(獵頭公司)去找,代理有候選人池,可根據企業的需求篩選出合適的候選人去回傳給企業,
7. 說一說設計模式中的配接器模式?
我們常常在開發中遇到各個系統之間的對接問題,然而每個系統的資料模型或多或少均存在差別,因此可能存在改變現有物件模型的情況,這將影響到系統的穩定,若想在不改變原有代碼結構(類的結構)的情況下完成友好對接,就需要用到配接器模式,
配接器模式(Adapter Pattern)通過定義一個配接器類作為兩個不兼容的介面之間的橋梁,將一個類的介面轉換成用戶期望的另一個介面,使得兩個或多個原本不兼容的介面可以基于配接器類一起作業,
配接器模式主要通過配接器類實作各個介面之間的兼容,該類通過依賴注人或者繼承實作各個介面的功能并對外統一提供服務,
在配接器模式的實作中有三種角色: Source、 Targetable、 Adapter, Source 是待適配的類,Targetable 是目標介面,Adapter 是配接器,我們在具體應用中通過Adapter 將Source的功能擴展到Targetable,以實作介面的兼容,配接器的實作主要分為三類:類配接器模式、物件配接器模式、介面配接器模式,
八、框架
1. Spring的特點?
Spring 是一款開源的輕量級 Java 開發框架,旨在提高開發人員的開發效率以及系統的可維護性,
我們一般說 Spring 框架指的都是 Spring Framework,它是很多模塊的集合,使用這些模塊可以很方便地協助我們進行開發,
比如說 Spring 自帶 IoC(Inverse of Control:控制反轉) 和 AOP(Aspect-Oriented Programming:面向切面編程)、可以很方便地對資料庫進行訪問、可以很方便地集成第三方組件(電子郵件,任務,調度,快取等等)、對單元測驗支持比較好、支持 RESTful Java 應用程式的開發,
2.談談自己對于 Spring IoC 的了解
IoC(Inverse of Control:控制反轉) 是一種設計思想,而不是一個具體的技術實作,IoC 的思想就是將原本在程式中手動創建物件的控制權,交由 Spring 框架來管理,不過, IoC 并非 Spirng 特有,在其他語言中也有應用,
控制:指的是物件創建(實體化、管理)的權力
反轉:控制權交給外部環境(Spring 框架、IoC 容器)
將物件之間的相互依賴關系交給 IoC 容器來管理,并由 IoC 容器完成物件的注入,這樣可以很大程度上簡化應用的開發,把應用從復雜的依賴關系中解放出來, IoC 容器就像是一個工廠一樣,當我們需要創建一個物件的時候,只需要配置好組態檔/注解即可,完全不用考慮物件是如何被創建出來的,
在實際專案中一個 Service 類可能依賴了很多其他的類,假如我們需要實體化這個 Service,你可能要每次都要搞清這個 Service 所有底層類的建構式,這可能會把人逼瘋,如果利用 IoC 的話,你只需要配置好,然后在需要的地方參考就行了,這大大增加了專案的可維護性且降低了開發難度,
在 Spring 中, IoC 容器是 Spring 用來實作 IoC 的載體, IoC 容器實際上就是個 Map(key,value),Map 中存放的是各種物件,
Spring 時代我們一般通過 XML 檔案來配置 Bean,后來開發人員覺得 XML 檔案來配置不太好,于是 SpringBoot 注解配置就慢慢開始流行起來,
3.Spring AOP的實作原理?具體應用在哪些方面?舉個例子?
AOP(Aspect-Oriented Programming:面向切面編程)能夠將那些與業務無關,卻為業務模塊所共同呼叫的邏輯或責任(例如事務處理、日志管理、權限控制等)封裝起來,便于減少系統的重復代碼,降低模塊間的耦合度,并有利于未來的可拓展性和可維護性,
Spring AOP 就是基于動態代理的,如果要代理的物件,實作了某個介面,那么 Spring AOP 會使用 JDK Proxy,去創建代理物件,而對于沒有實作介面的物件,就無法使用 JDK Proxy 去進行代理了,這時候 Spring AOP 會使用 Cglib ,這時候 Spring AOP 會使用 Cglib 生成一個被代理物件的子類來作為代理
4.Spring 框架中用到了哪些設計模式?
工廠設計模式: Spring 使用工廠模式通過 BeanFactory、ApplicationContext 創建 bean 物件,
代理設計模式: Spring AOP 功能的實作,
單例設計模式: Spring 中的 Bean 默認都是單例的,
模板方法模式: Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 結尾的對資料庫操作的類,它們就使用到了模板模式,
包裝器設計模式: 我們的專案需要連接多個資料庫,而且不同的客戶在每次訪問中根據需要會去訪問不同的資料庫,這種模式讓我們可以根據客戶的需求能夠動態切換不同的資料源,
觀察者模式: Spring 事件驅動模型就是觀察者模式很經典的一個應用,
配接器模式: Spring AOP 的增強或通知(Advice)使用到了配接器模式、spring MVC 中也是用到了配接器模式適配Controller,
九、Redis
1. redis都有哪些資料結構?
-
String字串:字串型別是 Redis 最基礎的資料結構,首先鍵都是字串型別,而且 其他幾種資料結構都是在字串型別基礎上構建的,我們常使用的 set key value 命令就是字串,常用在快取、計數、共享Session、限速等,
-
Hash哈希:在Redis中,哈希型別是指鍵值本身又是一個鍵值對結構,哈希可以用來存放用戶資訊,比如實作購物車,
-
List串列(雙向鏈表):串列(list)型別是用來存盤多個有序的字串,可以做簡單的訊息佇列的功能,
-
Set集合:集合(set)型別也是用來保存多個的字串元素,但和串列型別不一 樣的是,集合中不允許有重復元素,并且集合中的元素是無序的,不能通過索引下標獲取元素,利用 Set 的交集、并集、差集等操作,可以計算共同喜好,全部的喜好,自己獨有的喜好等功能,
-
Sorted Set有序集合(跳表實作):Sorted Set 多了一個權重引數 Score,集合中的元素能夠按 Score 進行排列,可以做排行榜應用,取 TOP N 操作,
2. Redis的應用場景
- 快取
- 共享Session
- 訊息佇列系統
- 分布式鎖
3. redis的延時佇列怎么實作?
1、使用zset資料結構存盤,訂單號為key,時間為score,
2、新增訂單的時候,將訂單號插入zset,
3、設定輪詢,每分鐘輪詢一次zset,找出score小于當前秒數的資料,進行處理,然后將key在zset內洗掉,
4. Redis如何實作持久化?
Redis支持RDB和AOF兩種持久化方式,
(1) RDB (Redis DataBase): rDB在指定的時間間隔內對資料進行快照存盤,RDB的特點在于:檔案格式緊湊,方便進行資料傳輸和資料恢復;在保存.rdb快照檔案時父行程會fork 出一個子行程,由子行程完成具體的持久化作業,所以可以最大化Redis 的性能;同時,與AOF相比,在恢復大的資料集時會更快一些,
(2) AOF ( Append Of Flie): AOF記錄對服務器的每次寫操作,在Redis重啟時會重放這些命令來恢復原資料,AOF命令以Redis 協議追加和保存每次寫操作到檔案末尾,Redis還能對AOF檔案進行后臺重寫,使得AOF檔案的體積不至于過大,AOF的特點有:可以使用不同的fsync 策略(無fsync、每秒fsync、每次寫的時候fsync )將操作追加命令到檔案中,操作效率高;同時,AOF檔案是日志的格式,更容易被理解和操作,
5. 什么是Redis的事務,用來干什么?
Redis事務就是一個命令執行的佇列,將一系列預定義命令包裝成一個整體,就是一個佇列,當執行的時候,一次性按照添加順序依次執行,中間不會被打斷或者干擾,
一個佇列中,一次性,順序性,排他性的執行一系列命令,
6. Redis是阻塞式IO嗎?怎么做到請求一個一個進行處理?
(1) 絕大部分請求是純粹的記憶體操作(非常快速)
(2) 采用單執行緒,避免了不必要的背景關系切換和競爭條件
(3) 非阻塞IO - IO多路復用
內部實作采用epoll,采用了epoll+自己實作的簡單的事件框架,epoll中的讀、寫、關閉、連接都轉化成了事件,然后利用epoll的多路復用特性,絕不在io上浪費一點時間 這3個條件不是相互獨立的,特別是第一條,如果請求都是耗時的,采用單執行緒吞吐量及性能可想而知了,應該說redis為特殊的場景選擇了合適的技術方案,
7. Redis快取淘汰策略?是失效時間到了就立即淘汰嗎?淘汰控制?
FIFO (First In First Out)先進先出原則
最先進入的快取資料在快取空間不夠的情況下(超出最大元素限制時)會首先被清理出去
LFU (Less Frequently Uesd)最少使用原則
一直以來最少被使用的元素會被清理掉,意味著,要求快取的元素有一個hit屬性,在快取空間不夠的情況下,hit值最小的將會被清理出去
LRU (Least Recently Used)最近最少使用原則
快取的元素有個時間戳,當快取容量滿了,而又要騰出新地方來快取新的元素的時候,則現有快取元素中時間戳離當前時間最遠的元素將被清除出去
8. 什么是快取雪崩和快取穿透?
快取雪崩
快取雪崩指在同一時刻由于大量快取失效,導致大量原本應該訪問快取的請求都去查詢資料庫,而對資料庫的CPU和記憶體造成巨大壓力,嚴重的話會導致資料庫宕機,從而形成一-系列連鎖反應,使整個系統崩潰,
快取穿透
快取穿透指由于級存系統故陳或者用戶頻繁查詢系統中不存在(在系統中不存在,在自然資料庫和級存中都不存在)的資料,而這時請水穿過快取不斷被發送到資料庫,導致資料庫過載,進而引發一連串非發問題,
9. 如何解決 Redis 快取雪崩問題
- 請求加鎖:對于并發量不是很多的應用,使用請求加鎖排隊的方案防止過多請求資料庫,
- 失效更新:為每一個快取資料都增加過期標記來記錄快取資料是否失效,如果快取標記失效,則更新資料快取,
- 設定不同的失效時間:為不同的資料設定不同的快取失效時間,防止在同-時刻有大量的資料失效,
10. 如何解決 Redis 快取穿透問題
常用的解決級存穿透問題的方法有布隆過濾器和eachenull策略,
-
布隆過池器:指將所有可能存在的資料都映射列一個足夠大的Bitmap中,在用戶發起請求時首先經過布隆過濾器的攔截,一個一定不存在的資料會被這個布隆過泄器攔做,從而避免對底層存盤系統業來企詢上的壓力,
-
cache null 策略:指如果一個查詢回傳的結果為null (可能見資料不存在,也可能是系統故陳),我們仍然級存這個nul結果,但它的過期時間會很短,通常不旭過5分鐘;在用戶再次青水該資料時血接回傳nul,而不會繼續訪問資料庫,從而有效保陳資料庫的安全,其實cache null 策略的核心原理是:在級存中記錄一個短暫的(資料過則時間內)資料在系統中是否存在的狀態,如果不存在,則直接回傳nl,不再在詢資料內,從而避免復存穿遇列資料庫上,
11. Redis如何實作分布式鎖
實作思路與注意事項:
- 設定合理的過期時間,解決忘記釋放鎖、甚至服務器宕機未釋放鎖的問題
- 獲取鎖和設定過期時間,需要具有原子性,使用指令
SET key value NX PX milliseconds
NX 代表只有當鍵key不存在的時候才會設定key的值
PX 表示設定鍵 key 的過期時間,單位是毫秒
- value 值隨機設定,洗掉 value 前判斷是否相等,解決當前執行緒可能釋放其他執行緒加的鎖的問題
- lua 腳本可以解決,洗掉 value 時判斷-洗掉,非原子操作的問題
十、Kafka
1. 問一下kafka的問題吧,kafka是怎么進行資料備份的?
2. 消費者是從leader中拿資料,還是從follow中拿資料?
3. 說說kafka的ISR機制?
4. kafka里面存的資料格式都是什么樣的?
5. kafka中存的一個是資料檔案,一個是索引檔案,說說這個?
6. kafka 是如何清理過期資料的?
7. 一條message中包含哪些資訊?
十一、專案
1. 校園論壇專案介紹
2. 這個專案最具挑戰的是什么?
3. 專案中如何使用多執行緒?
4. 登錄注冊是如何實作的?用Cookie做了什么? Cookie被竊取了該怎么辦?
5. Kafka做什么的?
6. 專案Kafka使用場景,為什么要用Kafka? Kafka為什么吞吐量高?
7. Kafka怎么保證順序性?
8. 說一下kafka的內部結構,訊息佇列存在的意義?
9. 如何做到顯示首頁的熱度最高帖子?如何更新快取?
10. Redis存了什么資料?快取過期時間是多少?如何解決快取一致性問題?
11. 如何識別熱點資料?熱度如何計算?如何更新熱度?
12. Redis如何淘汰過期資料?
13. 專案里redis做持久化處理了嗎
14. redis宕機了怎么辦
15. zset的底層資料結構?什么是跳表?
15. ES如何實作全文搜索的功能?ES的底層資料結構?
16. ES倒排索引為什么能夠加速搜索?
17. 專案中SpringSecurity的權限模型是怎么樣的?
18 .用戶的授權資訊如何存盤?
十二、演算法
1. 穩定排序有哪些?
不穩定:快排,堆排序,希爾排序,直接選擇排序,
穩定:直接插入排序,冒泡,歸并排序,基數排序
2. 排序的時空復雜度

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/299161.html
標籤:其他
上一篇:ZooKeeper監控資料采集方案——Telegraf Plugin
下一篇:2021-09-09
