面試題真的是博大精深,也通過這個面試題學到了很多東西,很多筆者也不是很懂,如有描述錯誤的地方還望大佬賜教,
每一次面試都可能問到相同的問題,一面問到,二三面還可能會問到,筆者認為這一點是整理這篇面試題識訓最大的一點,文末有面試題整理,以及答案,
目錄:
一面
1.1、HashMap和Hashtable的區別
1.2、實作一個保證迭代順序的HashMap
1.3、 說一說排序演算法,穩定性,復雜度
1.4、 說一說GC
1.5、 可以保證的實習時長
1.6、 職業規劃
二面
2.1、 自我介紹,
2.2、 JVM如何加載一個類的程序,雙親委派模型中有哪些方法?
2.3、 HashMap如何實作的?
2.4、 HashMap和Concurrent HashMap區別, Concurrent HashMap 執行緒安全嗎, Concurrent HashMap如何保證 執行緒安全?
2.5、 HashMap和HashTable 區別,HashTable執行緒安全嗎?
2.6、 行程間通信有哪幾種方式?
2.7、 JVM分為哪些區,每一個區干嗎的?
2.8、 JVM如何GC,新生代,老年代,持久代,都存盤哪些東西?
2.9、 GC用的參考可達性分析演算法中,哪些物件可作為GC Roots物件?
2.10、 快速排序,程序,復雜度?
2.11、 什么是二叉平衡樹,如何插入節點,洗掉節點,說出關鍵步驟,
2.12、 TCP如何保證可靠傳輸?三次握手程序?
2.13、 TCP和UDP區別?
2.14、 滑動視窗演算法?
2.15、 Linux下如何進行行程調度的?
2.16、 Linux下你常用的命令有哪些?
2.17、 作業系統什么情況下會死鎖?
2.18、 常用的hash演算法有哪些?
2.19、 什么是一致性哈希?
2.20、 如何理解分布式鎖?
2.21、 資料庫中的范式有哪些?
2.22、 資料庫中的索引的結構?什么情況下適合建索引?
2.23、 Java中的NIO,BIO,AIO分別是什么?
2.24、 用什么工具除錯程式?JConsole,用過嗎?
2.25、 現在JVM中有一個執行緒掛起了,如何用工具查出原因?
2.26、 執行緒同步與阻塞的關系?同步一定阻塞嗎?阻塞一定同步嗎?
2.27、 同步和異步有什么區別?
2.28、 執行緒池用過嗎?
2.29、 如何創建單例模式?說了雙重檢查,他說不是執行緒安全的,如何高效的創建一個執行緒安全的單例?
2.30、 concurrent包下面,都用過什么?
2.31、 常用的資料庫有哪些?redis用過嗎?
2.32、 了解hadoop嗎?說說hadoop的組件有哪些?說下mapreduce編程模型,
2.33、 你知道的開源協議有哪些?
2.34、 你知道的開源軟體有哪些?
2.35、 你最近在看的書有哪些?
2.36、 你有什么問題要問我嗎?
2.37、 了解哪些設計模式?說說都用過哪些設計模式
2.38、 如何判斷一個單鏈表是否有環?
2.39、 作業系統如何進行分頁調度?
2.40、 匿名內部類是什么?如何訪問在其外面定義的變數?
三面
3.1、 自我介紹,做過什么專案,
3.2、java虛擬機的區域如何劃分,
3.3、 雙親委派模型中,從頂層到底層,都是哪些類加載器,分別加載哪些類?
3.4、 有沒有可能父類加載器和子類加載器,加載同一個類?如果加載同一個類,該使用哪一個類?
3.5、 HashMap的結構,get(),put()是如何實作的?
3.6、 ConcurrentHashMap的get(),put(),又是如何實作的?ConcurrentHashMap有哪些問題? ConcurrentHashMap的鎖是讀鎖還是寫鎖?
3.7、 sleep()和wait()分別是哪個類的方法,有什么區別?synchronized底層如何實作的?用在代碼塊和方法上有什么區別?
3.8、 什么是執行緒池?如果讓你設計一個動態大小的執行緒池,如何設計,應該有哪些方法?
3.9、 什么是死鎖?JVM執行緒死鎖,你該如何判斷是因為什么?如果用VisualVM,dump執行緒資訊出來,會有哪些資訊?
3.10、 查看jvm虛擬機里面堆、執行緒的資訊,你用過什么命令?
3.11、 垃圾回收演算法有哪些?CMS知道嗎?如何作業的?
3.12、 資料庫中什么是事務?事務的隔離級別?事務的四個特性?什么是臟讀,幻讀,不可重復讀?
3.13、 資料庫索引的結構有哪些? 介紹B+樹的結構,
3.14、 資料庫中的分頁查詢陳述句怎么寫?
3.15、 什么是一致性哈希?用來解決什么問題?
3.16、 Redis的存盤結構,或者說如何作業的,與mysql的區別?有哪些資料型別?
3.17、 專案中用到redis,為什么選用redis,了解其他NoSQL資料庫嗎?在你的專案中是如何運用redis的?key是什么,value是什么?
3.18、 歸并排序的程序?時間復雜度?空間復雜度?你平常用什么排序?快速排序,說說在那些場景下適用,哪些場景下不適用,
3.19、 Solr是如何作業的?
一面
1.1、HashMap和Hashtable的區別
繼承:
Hashtable繼承自Dictionary類,而HashMap繼承自AbstractMap類,但二者都實作了Map介面,
鎖:
Hashtable 中的方法是Synchronize的,而HashMap中的方法在預設情況下是非Synchronize的,
方法:
HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey,因為contains方法容易讓人引起誤解,
Hashtable則保留了contains,containsValue和containsKey三個方法,其中contains和containsValue功能相同,
是否可以為null:
Hashtable中,key和value都不允許出現null值,
HashMap中,null可以作為鍵,這樣的鍵只有一個,
Hashtable中有類似put(null,null)的操作,編譯同樣可以通過,因為key和value都是Object型別,但運行時會拋出NullPointerException例外,這是JDK的規范規定的,
Tips:
當get()方法回傳null值時,可能是 HashMap中沒有該鍵,也可能使該鍵所對應的值為null,因此,在HashMap中不能由get()方法來判斷HashMap中是否存在某個鍵, 而應該用containsKey()方法來判斷,
遍歷:
Hashtable、HashMap都使用了 Iterator,但是,Hashtable還使用了Enumeration的方式 ,
計算Hash值:
HashTable直接使用物件的hashCode,
HashMap的Hash值:(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
容量:
HashTable在不指定容量的情況下的默認容量為11,而HashMap為16,
Hashtable不要求底層陣列的容量一定要為2的整數次冪,而HashMap則要求一定為2的整數次冪,
Hashtable擴容時,將容量變為原來的2倍加1,而HashMap擴容時,將容量變為原來的2倍,
1.2、實作一個保證迭代順序的HashMap
使用HashMap的一個子類LinkedHashMap(順序遍歷的HashMap)進行研究
放入方法中:
重寫了最關鍵的Node<K, V> newNode(int hash, K key, V value, Node next)方法,該方法首先創建一個HashMap.Node的子類LinkedHashMap.Entry,它比Node多了兩個指標Entry<K, V> before, after;//用于保存前后兩個節點的資訊,
Tips:
LinkedHashMap擁有兩個瞬時的屬性
transient LinkedHashMap.Entry<K,V> tail;//用于保存上一個元素,即表尾;
transient LinkedHashMap.Entry<K, V> head;//用于保存第一個元素,即表頭,
遍歷:
使用LinkedEntryIterator迭代器進行遍歷,繼承于 abstract class LinkedHashIterator抽象類,該迭代器擁有兩個Entry的指標next和current,并結合Entry中的before和after指標,實作了LinkedHashMap中元素的順序遍歷,
LinkedHashIterator的nextNode方法使用了LinkedHashMap.Entry<K, V>的after屬性,使得iterator的遍歷按照放入順序進行的,
取值方法:
LinkedHashMap重寫了Map介面的V get(Object key)方法,該方法分兩個步驟:
呼叫父類HashMap的getNode(hash(key), key)方法,獲取value;
如果accessOrder(訪問后重排序)為true(默認為false),那么移動所訪問的元素到表尾,并修改head和tail的值,
1.3、 說一說排序演算法,穩定性,復雜度
在這里插入圖片描述
這個東西還是面試前把每個排序演算法都看一看比較好
1.4、 說一說GC
堆(新生代和老生代)是Java虛擬機進行垃圾回收的主要場所,其次要場所是方法區(永久代),
在堆中進行垃圾回收分為新生代和老生代;將新生代分成了三個獨立的區域(這里的獨立區域只是一個相對的概念,并不是說分成三個區域以后就不再互相聯合作業了),
分別為:Eden區、From Survivor區以及To Survivor,而Eden區分配的記憶體較大,其他兩個區較小,每次使用Eden和其中一塊Survivor,
在進行垃圾回收時,將Eden和Survivor中還存活著的物件進行一次性地復制到另一塊Survivor空間上,直到其兩個區域中物件被回收完成,
當Survivor空間不夠用時,需要依賴其他老年代的記憶體進行分配擔保,當另外一塊Survivor中沒有足夠的空間存放上一次新生代收集下來的存活物件時,這些物件將直接通過分配擔保機制進入老生代,大物件和長期存活的物件也會直接進入老年代,
如果老生代的空間也被占滿,當來自新生代的物件再次請求進入老生代時就會報OutOfMemory例外,
新生代中的垃圾回收頻率高, ,保存在JVM的方法區(永久代)中的物件一般不會被回收,
其永久代進行垃圾回收的頻率就較低,速度也較慢,
永久代的垃圾收集主要回收廢棄常量和無用類,
判斷一個類是否被回收,則需同時滿足的條件:
該類所有的實體和ClassLoader都已經被回收,
該類的物件沒有被參考,無法通過反射訪問,這里說的是可以回收而不是必然回收,
大多數情況下,物件在新生代Eden區中分配,當Eden區沒有足夠空間進行分配時,虛擬機將發起一次Minor GC;
同理,當老年代中沒有足夠的記憶體空間來存放物件時,虛擬機會發起一次Major GC/Full GC,只要老年代的連續空間大于新生代物件總大小或者歷次晉升的平均大小就會進行Minor GC,否則將進行Full CG,
虛擬機通過物件年齡計數器來判斷存放在哪:
如果物件在Eden出生并經過一次Minor GC后仍然存活,并且能被Survivor容納的話,將被移動到Survivor空間中,并將該物件的年齡設為1,
物件每在Survivor中熬過一次Minor GC,年齡就增加1歲,當他的年齡增加到最大值15(MaxTenuringThreshold)時,就將會被晉升到老年代中,
如果在Survivor空間中所有相同年齡的物件大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的物件就可以直接進入老年代,無需等到MaxTenuringThreshold中要求的年齡,
Jdk8開始廢棄永久代:
This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.
(移除永久代是為融合HotSpot JVM與 JRockit VM而做出的努力,因為JRockit沒有永久代,不需要配置永久代,)
由于永久代記憶體經常不夠用或發生記憶體泄露,爆出例外java.lang.OutOfMemoryError: PermGen
字串存在永久代中,容易出現性能問題和記憶體溢位,
類及方法的資訊等比較難確定其大小,因此對于永久代的大小指定比較困難,太小容易出現永久代溢位,太大則容易導致老年代溢位,
永久代會為GC帶來不必要的復雜度,而且回收效率偏低,
1.5、 可以保證的實習時長
一般都是半年到一年,(太少的話,剛教會了你,你就可能走了,太多的話也不現實)
還有可能問到什么時候去上班,建議的話,就是下個月,或者兩周后,今天面試明天上班,肯定準備的不充分,(問這個的話,大多都是有個專案什么的著急要人,然后面試官用你應付,)
1.6、 職業規劃
其實這個問題不只是回答面試官,更是回答自己,為什么做,想怎么做……
說明自己對崗位的理解和從事這份作業的原因,
說明自己愿意為這份作業付出努力,
說明自己長遠的目標和規劃,
下面以產品經理為例子回答:
互聯網行業是一個高速發展的行業,同時也有大量創新和嘗試的機會(闡述自己看好行業),
而產品經理則是互聯網企業的核心崗位之一,產品經理負責用戶需求分析、競品分析、產品設計和上下層需求的溝通,需要超強的邏輯思考和分析能力、用戶洞察能力和溝通協作能力,(闡述自己對崗位的理解),
而我畢業于XXX大學,在大學里曾參加XXX產品設計比賽,拿下了XXX的成績,個人非常擅長思考和分析問題,同時能處理好和團隊成員的溝通協作…(闡述自己適合這個作業),
我認為自己非常適合這個崗位,為此,我也愿意付出努力,
在過去,我曾閱讀過XXX本產品書籍,自己設計過3款產品的原型,有一款在自己的努力下成功上線,并通過持續獲取用戶反饋,識訓了XXX萬的用戶,(表達自己過去的努力)
入職以后,我希望能從助理開始,系統學習產品的基本功,在一年的時間里面,成功掌握主流的產品設計方法論(表達自己愿意付出的努力),
我知道,優秀的產品經理不僅僅需要掌握產品設計的方法,還需要XXXX,
我會努力培養自己的業務思維,站在全域業務的角度去思考和解決問題,為團隊做好表率…(表達自己大致的努力方向),
每個產品經理都有自己的目標,我也一樣,
我希望在我的努力之下,在兩年以后,能夠獨擋一面,負責好一個版塊的功能;
在三到五年左右,可以負責好一個產品的規劃、設計和優化;
在未來的五到八年,可以做好一個產品的全域規劃、團隊管理等等…
二面
2.1、 自我介紹,
自我介紹在三到五分鐘最好
一兩句話概括自己名字學校什么的,主學的什么,對什么有研究,了解什么(切忌:盡量別說“精通”),然后說一下以前做過的專案(具體說一些自己做的有技術的),或者什么別的獎項,然后談一下自己對公司的了解以及對未來的規劃,等等,(百度有很多,隨便搜搜)
2.2、 JVM如何加載一個類的程序,雙親委派模型中有哪些方法?
類加載程序:
加載
(通過一個類的全限定名獲取定義此類的二進制位元組流,將這個位元組流所代表的靜態存盤結構轉化為方法區域的運行時資料結構,在Java堆中生成一個代表這個類的java.lang.Class物件,作為方法區域資料的訪問入口)、
驗證
(驗證階段作用是保證Class檔案的位元組流包含的資訊符合JVM規范,不會給JVM造成危害,如果驗證失敗,就會拋出一個java.lang.VerifyError例外或其子類例外,
1.檔案格式驗證:
驗證位元組流檔案是否符合Class檔案格式的規范,并且能被當前虛擬機正確的處理,
2.元資料驗證:
是對位元組碼描述的資訊進行語意分析,以保證其描述的資訊符合Java語言的規范,
3.位元組碼驗證:
主要是進行資料流和控制流的分析,保證被校驗類的方法在運行時不會危害虛擬機,
4.符號參考驗證:
符號參考驗證發生在虛擬機將符號參考轉化為直接參考的時候,這個轉化動作將在決議階段中發生,)、
準備
(準備階段為(static)變數(不包括類的實體)分配記憶體并設定類變數的初始化,此初始化并不是賦值static int num =1,這時得num為0,并不是1)、
決議
(決議程序是將常量池內的符號參考替換成直接參考(類或介面的決議、欄位決議、方法決議、介面方法決議,))、
初始化
(這里才是賦值階段)
使用程序:新執行緒—程式計數器----jvm堆疊執行(物件參考)-----堆記憶體(直接參考)----方法區,
卸載靠GC
雙親委派模型中方法:
雙親委派是指如果一個類收到了類加載的請求,不會自己先嘗試加載,先找父類加載器去完成,當頂層啟動類加載器表示無法加載這個類的時候,子類才會嘗試自己去加載,當回到最開的發起者加載器還無法加載時,并不會向下找,而是拋出ClassNotFound例外,
方法:啟動(Bootstrap)類加載器,標準擴展(Extension)類加載器,應用程式類加載器(Application ),背景關系(Custom)類加載器,意義是防止記憶體中出現多份同樣的位元組碼 ,
1)啟動類加載器(Bootstrap ClassLoader):
負責加載JAVA_HOME\lib目錄中并且能被虛擬機識別的類別庫到JVM記憶體中,如果名稱不符合的類別庫即使放在lib目錄中也不會被加載,該類加載器無法被Java程式直接參考,
2)擴展類加載器(Extension ClassLoader):
按《深入理解java虛擬機》這本書上所說,該加載器主要是負責加載JAVA_HOME\lib\ext目錄中的類別庫,但是貌似在JDK的安裝目錄下,沒看到該指定的目錄,該加載器可以被開發者直接使用,
3)應用程式類加載器(Application ClassLoader):
該類加載器也稱為系統類加載器,它負責加載用戶類路徑(Classpath)上所指定的類別庫,開發者可以直接使用該類加載器,如果應用程式中沒有自定義過自己的類加載器,一般情況下這個就是程式中默認的類加載器,
2.3、 HashMap如何實作的?
關于HashMap還是看一下這一篇比較好
HashMap的底層是陣列+鏈表,(很多人應該都知道了)
JDK1.7的是陣列+鏈表
首先是一個陣列,然后陣列的型別是鏈表
元素是頭插法
JDK1.8的是陣列+鏈表 或者 陣列+紅黑樹
首先是一個陣列,然后陣列的型別是鏈表
在鏈表的元素大于8的時候,會變成紅黑樹
(當鏈表長度大于8并且陣列長度大于64時,才會轉換為紅黑樹,
如果鏈表長度大于8,但是陣列長度小于64時,還是會進行擴容操作,不會轉換為紅黑樹,因為陣列的長度較小,應該盡量避開紅黑樹,因為紅黑樹需要進行左旋,右旋,變色操作來保持平衡,
所以當陣列長度小于64,使用陣列加鏈表比使用紅黑樹查詢速度要更快、效率要更高, )
在紅黑樹的元素小于6的時候會變成鏈表
(這里需要注意,不是元素小于6的時候一定會變成鏈表,只有resize的時候才會根據UNTREEIFY_THRESHOLD 進行轉換,同樣也不是到8的時候就變成紅黑樹(不是等到擴容的時候) 鏈表與紅黑樹的轉換詳情)
元素進行尾插
2.4、 HashMap和Concurrent HashMap區別, Concurrent HashMap 執行緒安全嗎, Concurrent HashMap如何保證 執行緒安全?
關于HashMap還是看一下這一篇比較好
使用ConcurrentHashMap(執行緒安全),
JDK1.7的是分段陣列,有Segment鎖(繼承于ReentrantLock)加速一小段保證并發
JDK1.8 是和HashMap一樣了,陣列+鏈表(或者紅黑樹)
Synchronized(鎖)and CAS(compare and swap)
(JVM在1.6對Synchronize的優化很好)
CAS通俗易懂,比較并替換
(CAS是一種無鎖演算法,CAS有3個運算元,記憶體值V,舊的預期值A,要修改的新值B,當且僅當預期值A和記憶體值V相同時,將記憶體值V修改為B,否則什么都不做)
(無鎖化的修改值的操作,他可以大大降低鎖代理的性能消耗,這個演算法的基本思想就是不斷地去比較當前記憶體中的變數值與你指定的 一個變數值是否相等,如果相等,則接受你指定的修改的值,否則拒絕你的操作,因為當前執行緒中的值已經不是最新的值,你的修改很可能會覆寫掉其他執行緒修改的結果,這一點與樂觀鎖,SVN的思想是比較類似的)
2.5、 HashMap和HashTable 區別,HashTable執行緒安全嗎?
1.1、HashMap和Hashtable的區別
HashTable(執行緒安全)就是把HashMap套上了一個Synchronized
2.6、 行程間通信有哪幾種方式?
管道、訊息佇列、信號量、共享記憶體、套接字
無名管道( pipe ):
管道是一種半雙工的通信方式,資料只能單向流動,而且只能在具有親緣關系的行程間使用,行程的親緣關系通常是指父子行程關系,
高級管道(popen):
將另一個程式當做一個新的行程在當前程式行程中啟動,則它算是當前程式的子行程,這種方式我們成為高級管道方式,
有名管道 (named pipe) :
有名管道也是半雙工的通信方式,但是它允許無親緣關系行程間的通信,
訊息佇列( message queue ) :
訊息佇列是由訊息的鏈表,存放在內核中并由訊息佇列識別符號標識,訊息佇列克服了信號傳遞資訊少、管道只能承載無格式位元組流以及緩沖區大小受限等缺點,
信號量( semophore ) :
信號量是一個計數器,可以用來控制多個行程對共享資源的訪問,它常作為一種鎖機制,防止某行程正在訪問共享資源時,其他行程也訪問該資源,因此,主要作為行程間以及同一行程內不同執行緒之間的同步手段,
信號 ( sinal ) :
信號是一種比較復雜的通信方式,用于通知接收行程某個事件已經發生,
共享記憶體( shared memory ) :
共享記憶體就是映射一段能被其他行程所訪問的記憶體,這段共享記憶體由一個行程創建,但多個行程都可以訪問,共享記憶體是最快的 IPC 方式,它是針對其他行程間通信方式運行效率低而專門設計的,它往往與其他通信機制,如信號兩,配合使用,來實作行程間的同步和通信,
套接字( socket ) :
套解口也是一種行程間通信機制,與其他通信機制不同的是,它可用于不同機器間的行程通信,
2.7、 JVM分為哪些區,每一個區干嗎的?
執行緒獨占 : 堆疊 , 本地方法堆疊 ,程式計數器
執行緒共享 : 堆 , 方法區
程式計數器PC
執行緒私有的
它可以看做是當前執行緒所執行的位元組碼的行號指示器
記憶體區域中唯一一個沒有規定任何OutOfMemoryError的區域
Java虛擬機堆疊
執行緒私有的
每個方法在執行的同時都會創建一個堆疊幀,用于存盤區域變數表、運算元堆疊、動態鏈接、方法出口等資訊
如果執行緒請求的堆疊深度大于虛擬機所允許的深度,將拋StackOverFlowError例外;
如虛擬機擴展時仍無法申請到足夠的記憶體,就會拋出OutOfMemoryError例外
本地方法堆疊
與虛擬機堆疊非常相似,區別是虛擬機堆疊為虛擬機執行Java方法服務,而本地方法堆疊則為虛擬機使用Native方法服務
也會拋出StackOverFlowError和OutOfMemoryError例外
Java堆
執行緒共享的
Java堆是GC管理的主要區域
在虛擬機啟動時創建
存放物件實體,幾乎所有的物件實體和陣列都在這里分配記憶體,
如果在堆中沒有記憶體完成實體分配,并且堆也無法再擴展時,將會拋出OutOfMemoryError例外
方法區
執行緒共享的
用于存盤已被虛擬機加載的類資訊、常量、靜態變數、即使編譯器編譯后的代碼等資料
當方法區無法滿足記憶體分配需求時,將拋出OutOfMemoryError例外
運行時常量池
是方法區的一部分
用于存放編譯器生成的各種字面量和符號參考
相對于Class檔案常量池的一個重要特征是,具備動態性
運行時常量池是方法區的一部分,自然受到方法區記憶體的限制,當常量池無法再申請到記憶體時會拋出OutOfMemoryError例外
2.8、 JVM如何GC,新生代,老年代,持久代,都存盤哪些東西?
1.4、 說一說GC
2.9、 GC用的參考可達性分析演算法中,哪些物件可作為GC Roots物件?
可達性分析演算法的思想:
從一個被稱為GC Roots的物件開始向下搜索,如果一個物件到GC Roots沒有任何參考鏈相連時,則說明此物件不可用,
在java中可以作為GC Roots的物件有以下幾種:
虛擬機堆疊中參考的物件、方法區類靜態屬性參考的物件、方法區常量池參考的物件、本地方法堆疊JNI參考的物件
雖然這些演算法可以判定一個物件是否能被回收,但是當滿足上述條件時,一個物件 不一定會被回收,當一個物件不可達GC Roots時,這個物件并不會馬上被回收,而是處于一個死緩的階段,若要被真正的回收需要經歷兩次標記,如果物件在可達性分析中沒有與GC Roots的參考鏈,那么此時就會被第一次標記并且進行一次篩選,篩選的條件是是否有必要執行finalize()方法,當物件沒有覆寫finalize()方法或者已經被虛擬機呼叫過,那么就認為是沒必要的,
如果該物件有必要執行finalize()方法,那么這個物件將會放在一個稱為F-Queue的佇列中,虛擬機會觸發一個finalize()執行緒去執行,此執行緒是低優先級的,并且虛擬機不會承諾一直等待它運行完,這還是因為如果finalize()執行緩慢或者發生了死鎖,那么就會造成F-Queue佇列一直等待,造成了記憶體回收系統的崩潰,GC對處于F-Queue中的物件進行第二次被標記,這時,該物件將被移除“即將回收”集合,等待回收,
2.10、 快速排序,程序,復雜度?
//快速排序
void quick_sort(int s[], int l, int r)
{
if (l < r)
{
//Swap(s[l], s[(l + r) / 2]); //將中間的這個數和第一個數交換 參見注1
int i = l, j = r, x = s[l];
while (i < j)
{
while(i < j && s[j] >= x) // 從右向左找第一個小于x的數
j--;
if(i < j)
s[i++] = s[j];
while(i < j && s[i] < x) // 從左向右找第一個大于等于x的數
i++;
if(i < j)
s[j--] = s[i];
}
s[i] = x;
quick_sort(s, l, i - 1); // 遞回呼叫
quick_sort(s, i + 1, r);
}
}
快速排序,分治遞回,對于每一段做如下處理:
從右向左第一個小于x的數放在原來左位置+1得那個地方,相反,從左向右第一個大于x的數放到原來右位置-1,一直到坐位置》=右位置,然后中間位置=原來左面的那個位置的數,在遞回呼叫(l,i-1)和(i+1,r)
2.11、 什么是二叉平衡樹,如何插入節點,洗掉節點,說出關鍵步驟,
對于每一個結點來說,子樹和右子樹都是二叉平衡樹,左右子樹的高度差不能大于1,如果插入洗掉使得高度差大于1了,就要進行旋轉操作
插入:
如果有當前結點就回傳false,就插入,如果還是二叉平衡樹就回傳true,插入的程序中如果不符合條件,就左旋右旋處理
洗掉:
(1)洗掉節點沒有左子樹,這種情況直接將洗掉節點的父節點指向洗掉節點的右子樹,
(2)洗掉節點沒有右子樹,這種情況直接將洗掉節點的父節點指向洗掉節點的左子樹,
(3)洗掉節點左右子樹都存在,可以采用兩種方式,
1:讓洗掉節點左子樹的最右側節點代替當前節點
2:讓洗掉節點右子樹的最左側節點代替當前節點
2.12、 TCP如何保證可靠傳輸?三次握手程序?
TCP為了提供可靠傳輸:
(1)首先,采用三次握手來建立TCP連接,四次握手來釋放TCP連接,從而保證建立的傳輸信道是可靠的,
(2)其次,TCP采用了連續ARQ協議(回退N,Go-back-N;超時自動重傳)(自動重傳請求(Automatic Repeat-reQuest,ARQ))來保證資料傳輸的正確性,使用滑動視窗協議來保證接方能夠及時處理所接收到的資料,進行流量控制,
(3)最后,TCP使用慢開始、擁塞避免、快重傳和快恢復來進行擁塞控制,避免網路擁塞,
在TCP/IP協議中,TCP協議提供可靠的連接服務,采用三次握手建立一個連接,
第一次握手: 建立連接時,客戶端發送syn包(syn=j)到服務器,并進入SYN_SEND狀態,等待服務器確認;
第二次握手: 服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手: 客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手, 完成三次握手,客戶端與服務器開始傳送資料.
2.13、 TCP和UDP區別?
TCP與UDP區別總結
1、TCP面向連接(如打電話要先撥號建立連接);UDP是無連接的,即發送資料之前不需要建立連接
2、TCP提供可靠的服務,也就是說,通過TCP連接傳送的資料,無差錯,不丟失,不重復,且按序到達;UDP盡最大努力交付,即不保證可靠交付,TCP通過校驗和,重傳控制,序號標識,滑動視窗、確認應答實作可靠傳輸,如丟包時的重發控制,還可以對次序亂掉的分包進行順序控制,
3、UDP具有較好的實時性,作業效率比TCP高,適用于對高速傳輸和實時性有較高的通信或廣播通信,
4.每一條TCP連接只能是點到點的;UDP支持一對一、一對多、多對一和多對多的互動通信,
5、TCP對系統資源要求較多,UDP對系統資源要求較少,
為什么UDP有時比TCP更有優勢?
UDP以其簡單、傳輸快的優勢,在越來越多場景下取代了TCP,如實時游戲,
(1)網速的提升給UDP的穩定性提供可靠網路保障,丟包率很低,如果使用應用層重傳,能夠確保傳輸的可靠性,
(2)TCP為了實作網路通信的可靠性,使用了復雜的擁塞控制演算法,建立了繁瑣的握手程序,由于TCP內置的系統協議堆疊中,極難對其進行改進,
采用TCP,一旦發生丟包,TCP會將后續的包快取起來,等前面的包重傳并接收到后再繼續發送,延時會越來越大,基于UDP對實時性要求較為嚴格的情況下,采用自定義重傳機制,能夠把丟包產生的延遲降到最低,盡量減少網路問題對游戲性造成影響,
2.14、 滑動視窗演算法?
大概意思:在一個陣列或者其他鏈表中,確認左端點和右端點,這中間用和或者其他的存起來,一個一個的向右移動右端點,這個程序中可能因不符合條件要把左端點也右移,在這個程序中一直記錄最大值或者最小值,(向右移動左端點就是在這個范圍的和或者其他記錄的數值刪去左端點這個值)
2.15、 Linux下如何進行行程調度的?
涼涼…………(這個不會,看也看不懂的那種)
1.先來先服務調度演算法
先來先服務(FCFS)調度演算法是一種最簡單的調度演算法,該演算法既可用于作業調度,也可用于行程調度,當在作業調度中采用該演算法時,
每次調度都是從后備作業佇列中選擇一個或多個最先進入該佇列的作業,將它們調入記憶體,為它們分配資源、創建行程,然后放入就緒
佇列,在行程調度中采用FCFS演算法時,則每次調度是從就緒佇列中選擇一個最先進入該佇列的行程,為之分配處理機,使之投入運行,
該行程一直運行到完成或發生某事件而阻塞后才放棄處理機,
2、基于優先級調度 (Priority Scheduling)
在優先級調度演算法中,每個行程都關聯一個優先級,內核將CPU分配給最高優先級的行程,具有相同優先級的行程,按照
先來先服務的原則進行調度,
Aging就是指逐漸提高系統中長時間等待的行程的
優先級,我們可以每15分鐘將等待行程的優先級加1,最終
經過一段時間,即便是擁有最低優先級的行程也會變成系統中最高優先級的行程,從而被執行,
優先級調度可以搶占式或者非搶占式的,當一個行程在Ready佇列中時,內核將它的優先級與正在CPU上執行的行程的優先級
進行比較,當發現這個新行程的優先級比正在執行的行程高時:對于搶占式內核,新行程會搶占CPU,之前正在執行的行程
轉入Ready佇列;對于非搶占式內核,新行程只會被放置在Ready佇列的頭部,不會搶占正在執行的行程,
3、短行程優先(SCBF–Shortest CPU Burst First)
最短CPU運行期優先調度演算法(SCBF–Shortest CPU Burst First)
該演算法從就緒佇列中選出下一個“CPU執行期最短”的行程,為之分配處理機,
最短作業優先調度是優先級調度的特例,在優先級調度中我們根據行程的優先級來進行調度,在最短作業優先調度中我們
根據作業的執行時間長短來調度,
4、輪轉法 (Round-Robin Scheduling) (RR)
前幾種演算法主要用于批處理系統中,不能作為分時系統中的主調度演算法,在分時系統中,都采用時間片輪轉法,
簡單輪轉法:系統將所有就緒行程按FIFO規則排隊,按一定的時間間隔把處理機分配給佇列中的行程,這樣,就緒
佇列中所有行程均可獲得一個時間片的處理機而運行,多級佇列方法:將系統中所有行程分成若干類,每類為一級,
RR調度演算法轉為分時系統設計,它與FCFS很像,但是加入了搶占,具體調度程序是:內核從Ready佇列中選取第一個行程,
將CPU資源分配給它,并且設定一個定時器在一個時間片后中斷該行程,調度Ready佇列中的下一行程,很明顯,RR調度
演算法是搶占式的,并且在該演算法的調度下,沒有一個行程能夠連續占用CPU超過一個時間片,從而達到了分時的目的,
5、高回應比優先調度演算法
(1) 如果作業的等待時間相同,則要求服務的時間愈短,其優先權愈高,因而該演算法有利于短作業.
(2) 當要求服務的時間相同時,作業的優先權決定于其等待時間,等待時間愈長,其優先權愈高,因而它實作的是先來先服務.
(3) 對于長作業,作業的優先級可以隨等待時間的增加而提高,當其等待時間足夠長時,其優先級便可升到很高, 從而也可獲得處理機.
該演算法照顧了短作業,且不會使長作業長期得不到服務
6、搶占式調度演算法
非搶占式調度演算法
為每一個被控物件建立一個實時任務并將它們排列成一輪轉佇列,調度程式每次選擇佇列中的第一個任務投入運行.該任務完成后便把它掛在輪轉佇列的隊尾等待下次調度運行.
非搶占式優先調度演算法.
實時任務到達時,把他們安排在就緒佇列的對首,等待當前任務自我終止或運行完成后才能被調度執行.
搶占式調度演算法
1)基于時鐘中斷的搶占式優先權調度演算法.
實時任務到達后,如果該任務的優先級別高于當前任務的優先級并不立即搶占當前任務的處理機,而是等到時鐘中斷到來時,調度程式才剝奪當前任務的執行,將處理機分配給新到的高優先權任務.
2)立即搶占的優先權調度演算法.
在這種調度策略中,要求作業系統具有快速回應外部時間中斷的能力.一旦出現外部中斷,只要當前任務未處于臨界區便立即剝奪當前任務的執行,把處理機分配給請求中斷的緊迫任務,實時行程調度,實時行程搶占當前,
2.16、 Linux下你常用的命令有哪些?
這個最好是多用用比較好,直接跳過看一下個題
1、顯示日期的指令: date
2、顯示日歷的指令:cal
3、簡單好用的計算器:bc
怎么10/100會變成0呢?這是因為bc預設僅輸出整數,如果要輸出小數點下位數,那么就必須要執行 scale=number ,那個number就是小數點位數,
4、重要的幾個熱鍵[Tab],[ctrl]-c, [ctrl]-d
[Tab]按鍵—具有『命令補全』不『檔案補齊』的功能
[Ctrl]-c按鍵—讓當前的程式『停掉』
[Ctrl]-d按鍵—通常代表著:『鍵盤輸入結束(End Of File, EOF 戒 End OfInput)』的意思;另外,他也可以用來取代exit
5、man
退出用q,
man -f man
6、資料同步寫入磁盤: sync
輸入sync,那舉在記憶體中尚未被更新的資料,就會被寫入硬碟中;所以,這個挃令在系統關機戒重新啟勱乀前, 徑重要喔!最好多執行幾次!
7、慣用的關機指令:shutdown
此外,需要注意的是,時間引數請務必加入指令中,否則shutdown會自動跳到 run-level 1 (就是單人維護的登入情況),這樣就傷腦筋了!底下提供幾個時間引數的例子吧:
重啟,關機: reboot, halt,poweroff
8、切換執行等級: init
Linux共有七種執行等級:
–run level 0 :關機
–run level 3 :純文本模式
–run level 5 :含有圖形介面模式
–run level 6 :重新啟動
使用init這個指令來切換各模式:
如果你想要關機的話,除了上述的shutdown -h now以及poweroff之外,你也可以使用如下的指令來關機:
9、改變檔案的所屬群組:chgrp
10、改變檔案擁有者:chown
他還可以頇便直接修改群組的名稱
11、改變檔案的權限:chmod
權限的設定方法有兩種, 分別可以使用數字或者是符號來進行權限的變更,
–數字型別改變檔案權限:
–符號型別改變檔案權限:
12、查看版本資訊等
13、變換目錄:cd
14、顯示當前所在目錄:pwd
15、建立新目錄:mkdir
不建議常用-p這個選項,因為擔心如果你打錯字,那么目錄名稱就回變得亂七八糟的
16、洗掉『空』的目錄:rmdir
17、檔案與目錄的顯示:ls
18、復制檔案或目錄:cp
19、移除檔案或目錄:rm
20、移動檔案與目錄,或更名:mv
21、取得路徑的檔案名與目錄名:basename,dirname
22、由第一行開始顯示檔案內容:cat
23、從最后一行開始顯示:tac(可以看出 tac 是 cat 的倒著寫)
24、顯示的時候,順道輸出行號:nl
25、一頁一頁的顯示檔案內容:more
26、與 more 類似,但是比 more 更好的是,他可以往前翻頁:less
27、只看頭幾行:head
28、只看尾幾行:tail
29、以二進制的放置讀取檔案內容:od
30、修改檔案時間或新建檔案:touch
31、檔案預設權限:umask
32、組態檔檔案隱藏屬性:chattr
33、顯示檔案隱藏屬性:lsattr
34、觀察檔案型別:file
35、尋找【執行擋】:which
36、尋找特定檔案:whereis
37、尋找特定檔案:locate
38、尋找特定檔案:find
39、壓縮檔案和讀取壓縮檔案:gzip,zcat
40、壓縮檔案和讀取壓縮檔案:bzip2,bzcat
41、壓縮檔案和讀取壓縮檔案:tar
2.17、 作業系統什么情況下會死鎖?
死鎖的4個必要條件
(1) 互斥條件: 一個資源每次只能被一個行程使用,
(2) 請求與保持條件: 一個行程因請求資源而阻塞時,對已獲得的資源保持不放,
(3) 不剝奪條件: 行程已獲得的資源,在末使用完之前,不能強行被剝奪,
(4) 回圈等待條件: 若干行程之間形成一種頭尾相接的回圈等待資源關系,
這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發生死鎖,
競爭不可剝奪資源
在系統中所配置的不可剝奪資源,由于它們的數量不能滿足諸行程運行的需要,會使行程在運行程序中,因爭奪這些資源而陷于僵局,
競爭臨時性資源
上面所說的列印機資源屬于可順序重復使用型資源,稱為永久資源,還有一種所謂的臨時資源,這是指由一個行程產生,被另一個行程使用,短時間后便無用的資源,故也稱為消耗性資源,
2.18、 常用的hash演算法有哪些?
加法Hash;位運算Hash;乘法Hash;除法Hash;查表Hash;混合Hash;
1
2.19、 什么是一致性哈希?
一致性Hash演算法將整個哈希值空間組織成一個虛擬的圓環,整個空間按順時針方向組織
圓環的正上方的點代表0,0點右側的第一個點代表1,以此類推,
0點的左側是2的32次方-1,把這個圓環叫做Hash環
然后把服務器ip或者主機名字作為關鍵字Hash,每個服務器都能確定位置,把資料進行相同的Hash算出的位置,順時針訪問的第一個就是對應的服務器
一致性Hash演算法對于節點的增減都只需重定位環空間中的一小部分資料,具有較好的容錯性和可擴展性,
2.20、 如何理解分布式鎖?
分布式鎖: 甲乙兩人購買時剩余量都顯示一個,如果同時下單可能會出現問題,這時就需要用到分布式鎖
分布式鎖是實作有序調度不同的行程,解決不同行程之間相互干擾的問題的技術手段
分布式鎖的應具備的條件
在分布式系統環境下,分布式鎖在同一個時間僅能被同一個行程訪問
高可用的獲取/釋放鎖
高性能的獲取/釋放鎖
具備鎖的重入性
具備鎖的失效機制,防止死鎖
具備非阻塞鎖的特性,即使沒有獲取鎖也能直接回傳結果
分布式鎖的實作有哪些
mechache:
利用mechache的add命令,改命令是原子性的操作,只有在key 不存在的情況下,才能add成功,也就意味著執行緒拿到了鎖
Redis:
和Mechache的實作方法相似,利用redis的setnx命令,此命令同樣是原子性的操作,只有在key不存在的情況下,add成功
zookeeper:
利用他的順序臨時節點,來實作分布式鎖和等待佇列,zookeeper的設計初衷就是為了實作分布式微服務的
使用Redis實作分布式鎖的思路
先去redis中使用setnx(商品id,數量) 得到回傳結果
這里的數量無所謂,它的作用就是告訴其他服務,我加上了鎖
發現redis中有數量,說明已經可以加鎖了
發現redis中沒有資料,說明已經獲得到了鎖
解鎖: 使用redis的 del商品id
鎖超時, 設定exprie 生命周期,如30秒, 到了指定時間,自定解鎖
三個致命問題
非原子性操作:setnx,宕機,expire
因為 setnx和expire不是原子性的,要么都成功要么都失敗, 一旦出現了上面的情況,就會導致死鎖出現
redis提供了原子性的操作 set ( key , value , expire)
誤刪鎖
假如我們的鎖的生命事件是30秒,結果我在30s內沒操作完,但是鎖被釋放了
jvm2拿到了鎖進行操作
jvm1 操作完成使用del,結果把jvm2 的鎖洗掉了
解決方法, 在洗掉之前,判斷是不是自己的鎖
redis提供了原子性的操作 set ( key ,threadId, expire)
超時為完成任務
增加一個守護執行緒,當快要超時,但是任務還沒執行完成,就增加鎖的時間
2.21、 資料庫中的范式有哪些?
第一范式----資料庫中的表(所有欄位值)都是不可分割的原子資料項,
第二范式----資料庫表中的每一列都和主鍵相關,而不能只和主鍵的某一部分相關,也就是說 一個表中只能只能包含一個,不能把多種資料保存在同一個表中,
第三范式----資料庫表中每一列資料都和主鍵直接相關,不能間接相關,
2.22、 資料庫中的索引的結構?什么情況下適合建索引?
資料庫中索引的結構是一種排序的資料結構,是通過B樹和變形的B+樹實作的,
適合建索引: 經常查詢,使用,用在表連接的欄位(經常更改的欄位不適合建索引)
2.23、 Java中的NIO,BIO,AIO分別是什么?
BIO: 同步并阻塞,服務器實作模式為一個連接一個執行緒,即客戶端有連接請求時服務器端就需要啟動一個執行緒進行處理,如果這個連接不做任何事情會造成不必要的執行緒開銷,當然可以通過執行緒池機制改善,BIO方式適用于連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,并發局限于應用中,JDK1.4以前的唯一選擇,但程式直觀簡單易理解,
NIO: 同步非阻塞,服務器實作模式為一個請求一個執行緒,即客戶端發送的連接請求都會注冊到多路復用器上,多路復用器輪詢到連接有I/O請求時才啟動一個執行緒進行處理,NIO方式適用于連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,并發局限于應用中,編程比較復雜,JDK1.4開始支持,
AIO: 異步非阻塞,服務器實作模式為一個有效請求一個執行緒,客戶端的I/O請求都是由OS先完成了再通知服務器應用去啟動執行緒進行處理.AIO方式使用于連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分呼叫OS參與并發操作,編程比較復雜,JDK7開始支持,
2.24、 用什么工具除錯程式?JConsole,用過嗎?
JConsole在JDK/bin目錄下面,對資源消耗和性能進行監控,提供圖表和可視化界面,占記憶體小(具體的一些還是自己多打開看一看就差不多了)
2.25、 現在JVM中有一個執行緒掛起了,如何用工具查出原因?
通過Javacore了解執行緒運行情況:
Javacore,也可以稱為“threaddump”或是“javadump”,它是 Java 提供的一種診斷特性,能夠提供一份可讀的當前運行的 JVM 中執行緒使用情況的快照,即在某個特定時刻,JVM 中有哪些執行緒在運行,每個執行緒執行到哪一個類,哪一個方法,
應用程式如果出現不可恢復的錯誤或是記憶體泄露,就會自動觸發 Javacore 的生成,而為了性能問題診斷的需要,我們也會主動觸發生成 Javacore,
在 AIX、Linux、Solaris 環境中,我們通常使用 kill -3 產生該行程的 Javacore,
2.26、 執行緒同步與阻塞的關系?同步一定阻塞嗎?阻塞一定同步嗎?
同步是個程序,阻塞是執行緒的一種狀態,多個執行緒操作共享變數時可能會出現競爭,這時需要同步來防止兩個以上的執行緒同時進入臨界區,在這個程序中,后進入臨界區的執行緒將阻塞,等待先進入的執行緒走出臨界區,
執行緒同步不一定發生阻塞!!!執行緒同步的時候,需要協調推進速度,互相等待和互相喚醒會發生阻塞,
同樣,阻塞也不一定同步,
2.27、 同步和異步有什么區別?
同步互動: 指發送一個請 求,需要等待回傳,然后 才能夠發送下一個請求,有個等待程序;
異步互動: 指發送一個請求,不需要等待回傳,隨時可以再發送下一個請求,即不需要等待,
區別: 一個需要等待,一個不需要等待,在部分情況下,我們的專案開發中都會優先選擇不需要等待的異步互動方式,
2.28、 執行緒池用過嗎?
執行緒池做的作業
主要是控制運行的執行緒的數量,處理程序中將任務放入佇列,然后在執行緒創建后啟動這些任務,如果執行緒數量超過了最大數量超出數量的執行緒排隊等候,等其他執行緒執行完畢,再從佇列中取出任務來執行,
執行緒池的優勢
執行緒復用、控制最大并發數、執行緒管理
(1)降低系統資源消耗,通過重用已存在的執行緒,降低執行緒創建和銷毀造成的消耗;
(2)提高系統回應速度,當有任務到達時,通過復用已存在的執行緒,無需等待新執行緒的創建便能立即執行;
(3)方便執行緒并發數的管控,因為執行緒若是無限制的創建,可能會導致記憶體占用過多而產生OOM,并且會造成cpu過度切換(cpu切換執行緒是有時間成本的(需要保持當前執行執行緒的現場,并恢復要執行執行緒的現場)),
(4)提供更強大的功能,延時定時執行緒池,
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
1、corePoolSize(執行緒池基本大小): 當向執行緒池提交一個任務時,若執行緒池已創建的執行緒數小于corePoolSize,即便此時存在空閑執行緒,也會通過創建一個新執行緒來執行該任務,直到已創建的執行緒數大于或等于corePoolSize時,(除了利用提交新任務來創建和啟動執行緒(按需構造),也可以通過 prestartCoreThread() 或 prestartAllCoreThreads() 方法來提前啟動執行緒池中的基本執行緒,)
2、maximumPoolSize(執行緒池最大大小): 執行緒池所允許的最大執行緒個數,當佇列滿了,且已創建的執行緒數小于maximumPoolSize,則執行緒池會創建新的執行緒來執行任務,另外,對于無界佇列,可忽略該引數,
3、keepAliveTime(執行緒存活保持時間): 當執行緒池中執行緒數大于核心執行緒數時,執行緒的空閑時間如果超過執行緒存活時間,那么這個執行緒就會被銷毀,直到執行緒池中的執行緒數小于等于核心執行緒數,
4、workQueue(任務佇列): 用于傳輸和保存等待執行任務的阻塞佇列,
5、threadFactory(執行緒工廠): 用于創建新執行緒,threadFactory創建的執行緒也是采用new Thread()方式,threadFactory創建的執行緒名都具有統一的風格:pool-m-thread-n(m為執行緒池的編號,n為執行緒池內的執行緒編號),
6、handler(執行緒飽和策略): 當執行緒池和佇列都滿了,再加入執行緒會執行此策略,
執行緒池為什么需要使用(阻塞)佇列?
1、因為執行緒若是無限制的創建,可能會導致記憶體占用過多而產生OOM,并且會造成cpu過度切換,
2、創建執行緒池的消耗較高, (執行緒池創建執行緒需要獲取mainlock這個全域鎖,影響并發效率,阻塞佇列可以很好的緩沖,)
執行緒池為什么要使用阻塞佇列而不使用非阻塞佇列?
阻塞佇列可以保證任務佇列中沒有任務時阻塞獲取任務的執行緒,使得執行緒進入wait狀態,釋放cpu資源,
當佇列中有任務時才喚醒對應執行緒從佇列中取出訊息進行執行,
使得在執行緒不至于一直占用cpu資源,
不用阻塞佇列也是可以的,不過實作起來比較麻煩而已,有好用的為啥不用呢?
如何配置執行緒池
CPU密集型任務: 盡量使用較小的執行緒池,一般為CPU核心數+1,
IO密集型任務: 可以使用稍大的執行緒池,一般為2*CPU核心數,
混合型任務: 可以將任務分成IO密集型和CPU密集型任務,
2.29、 如何創建單例模式?說了雙重檢查,他說不是執行緒安全的,如何高效的創建一個執行緒安全的單例?
餓漢模式
通過定義final型的物件,來讓加載類的時候,直接創建物件,只加載一次,實作單例,
懶漢式
通過定義靜態物件,加鎖去實體化物件,
列舉
通過定義列舉類,來實作單例,
Double-Check
若有兩個執行緒通過了第一個Check回圈,進入第二個Check回圈是串行化的,只能有一個執行緒進入,這樣當這個執行緒創建完成后,另外的執行緒就無法通過第二個回圈了,保證了實體的唯一性,隨后的執行緒也不會通過第一個Check回圈,也就不會有同步控制的環節了,但是,這種方法也伴隨著一個缺點,它可能會引起空指標的例外,
高效創建執行緒安全的單例
Volatile+Double-Check
volatile關鍵字可以防止重排序的發生,在此不對volatile作詳細介紹,通過volatile關鍵字,這種模式可以說是滿足懶加載、多執行緒下單例的唯一性、安全性的,
public final class SingletonObject5 {
private volatile static SingletonObject5 instance ;
private SingletonObject5() {
}
public static SingletonObject5 getInstance() {
if (null == instance) {
synchronized (SingletonObject5.class) {
if (null == instance)
instance = new SingletonObject5();
}
}
return SingletonObject5.instance;
}
}
2.30、 concurrent包下面,都用過什么?
(涼涼夜色為我思念成河……)
1.executor介面,使用executor介面的子介面ExecutorService用來創建執行緒池
2.Lock介面下的ReentrantLock類,實作同步,比如三個執行緒回圈列印ABCABCABC…
3.atomic包,使用AtomicInteger類的incrementAndGet()方法來實作原子操作,比如a++
4.Callable介面,重寫call方法,實作多執行緒
5.concarrenHashMap,執行緒安全的HashMap
2.31、 常用的資料庫有哪些?redis用過嗎?
mysql 、SQL Server、Oracle、Sybase、DB2等
Redis:
1、純記憶體操作
2、核心是基于非阻塞的IO多路復用機制
3、單執行緒反而避免了多執行緒的頻繁背景關系切換問題
2.32、 了解hadoop嗎?說說hadoop的組件有哪些?說下mapreduce編程模型,
common、Hadoop Distributed File System(HDFS)、MapReduce、YARN
1
編程模型:
Mapper把復雜的任務分解為若干個小任務,分到存在所需資料結點上進行計算,這些任務可以一起,彼此沒有依賴關系
Reducer把Mapper的結果匯總
eg:
一共有100個漢堡,甲吃十個,乙吃是個,丙吃十個,,,這就是Mapper
甲乙丙……放到一起就是Reducer
2.33、 你知道的開源協議有哪些?
? Mozilla Public License:
MPL License,允許免費重發布、免費修改,但要求修改后的代碼著作權歸軟體的發起
者,這種授權維護了商業軟體的利益,它要求基于這種軟體得修改無償貢獻著作權給該軟體,這樣,圍繞該軟體得
所有代碼得著作權都集中在發起開發人得手中,但MPL是允許修改,無償使用得,MPL軟體對鏈接沒有要求,
? BSD開源協議:
給于使用者很大自由的協議,可以自由的使用,修改源代碼,也可以將修改后的代碼作為開源或
者專有軟體再發布,
? Apache Licence 2.0 :
Apache Licence是著名的非盈利開源組織Apache采用的協議,該協議和BSD類似,同樣
鼓勵代碼共享和尊重原作者的著作權,同樣允許代碼修改,再發布(作為開源或商業軟體),
? GPL:
GPL許可證是自由軟體的應用最廣泛的軟體許可證,人們可以修改程式的一個或幾個副本或程式的任何部
分,以此形成基於這些程式的衍生作品,必須在修改過的檔案中附有明顯的說明:您修改了此一檔案及任何修改
的日期, 您必須讓您發布或出版的作品,包括本程式的全部或一部分,或內含本程式的全部或部分所衍生的作
品,允許第三方在此許可證條款下使用,并且不得因為此項授權行為而收費,
? LGPL:
LGPL是GPL的一個為主要為類別庫使用設計的開源協議,和GPL要求任何使用/修改/衍生之GPL類別庫的的軟
件必須采用GPL協議不同,LGPL允許商業軟體通過類別庫參考(link)方式使用LGPL類別庫而不需要開源商業軟體的代
碼,這使得采用LGPL協議的開源代碼可以被商業軟體作為類別庫參考并發布和銷售,
? Public Domain:
公共域授權,將軟體授權為公共域,這些軟體包沒有授權協議,任何人都可以隨意使用它
2.34、 你知道的開源軟體有哪些?
? JDK
? eclipse
? Tomcat
? Spring
? Hibernate
? MySQL
? MyBatis
? struts
2.35、 你最近在看的書有哪些?
這個盡量說真話,最好不要搜一點簡介就說看過什么書,否則被戳穿很難受,
如果實在沒看過可以這么說:
最近沒怎么看書,但是相對于書本,我更喜歡在B站學習,比如大學公開課和攝影欄目我就經常逛;知乎我也挺活躍的,關注……等話題,每天保持相當的閱讀量,回答受贊也有幾百了;我也參加了很多經驗分享活動,我覺得從面對面的交流獲得的知識,是閱讀無法替代的,
2.36、 你有什么問題要問我嗎?
嚴禁:沒問題,提薪資,五年計劃,某某產品被賣了等無關緊要的問題
準備3-5個問題就行,太多或者太少都不好
問題
1、問作業內容
這份作業比較大的挑戰是?
您希望我在短期內解決哪些問題?
對于未來加入這個團隊,您對我的期望是什么?
我對這個職位作業的理解是XXX,不知道除了我的理解外,是否還有其他的作業職責?
2、問職位差距
對我此次應聘的表現有什么評價和建議?
如果可以錄用,我需要學習哪方面的知識?
接下來的這段空檔期,有什么值得注意或者建議學習的嗎?
3、問作業潛力
請問該崗位都要經歷哪些培訓?
這個崗位的晉升路徑是什么樣子的?
咱們部門近期/未來有什么新動向/新舉措?
您對這個崗位三到五年職業規劃的建議是什么呢
4、問團隊氛圍
能帶我看一下辦公區嗎?
您在公司的一天是如何度過的?
可以介紹下一起作業的團隊是什么樣的嗎?
提問的原則
1、探討式發問
提問不是只問不答,提問后,先拋出一點自己的理解,讓面試官看出你對應聘的職位是做過功課的,透過發問,更了解公司的組織文化和作業上實際會遇到的問題,
2、在其位謀其職
提問要展現專業度,切忌太跳脫或者故意裝高深,引發尷尬,又給人好高騖遠的感覺,記住一點:不提和所聘崗位無關的問題,
3、真誠
提問環節也是考驗情商的時候!關注他人感受,掌握好分寸感,要明白提問不是辯論,你不是為了和面試官互相切磋知識、技能,而是真誠、虛心請教,
2.37、 了解哪些設計模式?說說都用過哪些設計模式
設計模式的分類
總體來說是三大類:
創建型模式 ,五種:工廠方法模式、 抽象工廠模式、單例模式、建造者模式、原型模式
結構性模式 ,共七種: 配接器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式,
行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介模式、解釋器模式,
其實還有兩類:并發型模式和執行緒池模式
2.38、 如何判斷一個單鏈表是否有環?
快慢指標查詢,快指標每次跳兩個,慢指標每次跳一個,如果兩指標相等時,就證明有環
環的入口:
用兩個指標,一個指向快慢指標相交點(這里就是慢指標走,慢指標在走快指標的一半就相當于快指標走的路了,還會到這個點),一個指向單鏈表的頭結點(模擬慢指標從頭走,也是走快指標的一半),一起走,當兩個指標相等時,就是環的入口,
數學思維:
設從單鏈表頭節點到環入口節點的距離是D,環入口到相交點的距離是X,設slow和fast第一次相遇時fast走了n圈環,slow走的距離為len,那么fast走的距離是2*len,可以得出下面的兩個等式:
len = D + X
2 * len = D + X + n * R
兩個等式相減可以的到:len = n * R - X
如果還不行的話,自己畫個帶環的單鏈表就明白了
2.39、 作業系統如何進行分頁調度?
筆者不懂,涼涼……
1.分頁的作用: 高效率地利用記憶體,可以運行比物理記憶體空間更大的程式;
2.分頁機制的原理: 利用兩級頁表將記憶體分割成4KB/頁的大小,強制記憶體對齊提高效率;
3.頁表結構: PDE與PTE在記憶體中各個位的主要作用,表項與頁之間的對應關系,
2.40、 匿名內部類是什么?如何訪問在其外面定義的變數?
匿名內部類:
沒有名字,通常用來簡化代碼,只能用一次(使用的話必須要繼承父類或者實作介面)
訪問在其外面定義的變數:
必須要final型別的變數(也可以不用final型別,但只能使用不能修改)
三面
3.1、 自我介紹,做過什么專案,
這,就不說了,某度一搜一堆,專案要挑自己做的最有水平的拿出來
3.2、java虛擬機的區域如何劃分
程式計數器(獨立記憶體)
當前執行緒所執行的位元組碼的行號指示器,
Java虛擬機堆疊(獨立記憶體)
每個方法從呼叫直至執行完成的程序,就對應著一個堆疊幀在虛擬機堆疊中入堆疊到出堆疊的程序,
本地方法堆疊(獨立記憶體)
本地方法堆疊則為虛擬機使用到的Native方法(百度說是Java中宣告的可呼叫的C/C++實作的方法)服務,
Java堆(共享記憶體): 存放物件實體
方法區(共享記憶體): 存盤已被虛擬區加載的類資訊、常量、靜態變數、即時編譯器編譯后的代碼等資料,
運行時常量池: 存放編譯期生成的各種字面量和符號參考,這部分內容將在類加載后進入方法區的運行時常量池中存放,
3.3、 雙親委派模型中,從頂層到底層,都是哪些類加載器,分別加載哪些類?
(1)啟動類加載器(Bootstrap ClassLoader)
這個類加載器負責將存放在JAVA_HOME/lib下的,或者被-Xbootclasspath引數所指定的路徑中的,并且是虛擬機識別的類別庫加載到虛擬機記憶體中,啟動類加載器無法被Java程式直接參考,
(2)擴展類加載器(Extension ClassLoader)
這個加載器負責加載JAVA_HOME/lib/ext目錄中的,或者被java.ext.dirs系統變數所指定的路徑中的所有類別庫,開發者可以直接使用擴展類加載器
(3)應用程式類加載器(Application ClassLoader)
這個加載器是ClassLoader中getSystemClassLoader()方法的回傳值,所以一般也稱它為系統類加載器,它負責加載用戶類路徑(Classpath)上所指定的類別庫,可直接使用這個加載器,如果應用程式沒有自定義自己的類加載器,一般情況下這個就是程式中默認的類加載器
3.4、 有沒有可能父類加載器和子類加載器,加載同一個類?如果加載同一個類,該使用哪一個類?
雙親委派,先在父類看能不能加載,如果能則由父加載,否則給子類加載
3.5、 HashMap的結構,get(),put()是如何實作的?
Put:
1、先將key和value封裝到Node節點中
2、底層會呼叫key的hashcode()方法,通過hash函式將hash值轉換為陣列下標,下標位置上如果沒有任何元素,就把該Node添加到該位置上(該下標處)
如果該下標處對應的位置上已經存在元素或鏈表(多于一個元素變成鏈表),那么就會拿著新節點的key與鏈表上的每一個人節點中的key進行equals,
1、 如果所有對比(equals)都回傳false,那么這個新節點將會被添加到鏈表的尾部,(大于8個就會轉換成紅黑樹)
2、 如果其中有一個對比(equals)回傳true,那么這個節點上的value將會被新節點的value覆寫,
Get:
1、底層會呼叫key的hashcode()方法,通過hash函式將hash值轉換為陣列下標,通過陣列下標快速定位到陣列的指定位置上,如果這個位置上沒有任何元素,那么回傳null,
2、如果這個位置上有單向鏈表(該位置上有元素,或者有紅黑樹),那么會拿著我們get(key)中的key和單向鏈表中的每個節點的key進行equals,如果說所有的equals都回傳false,那么這個get方法回傳false,
3、只要其中有一個節點的key和引數key的equals對比的結果回傳true,那么這個節點的value就是我們想要找的value,get方法回傳這個value.
3.6、 ConcurrentHashMap的get(),put(),又是如何實作的?ConcurrentHashMap有哪些問題? ConcurrentHashMap的鎖是讀鎖還是寫鎖?
put 操作一上來就鎖定了整個segment,這當然是為了并發的安全,修改資料是不能并發進行的,必須得有個判斷是否超限的陳述句以確保容量不足時能夠 rehash,
而比較難懂的是這句int index = hash & (tab.length - 1),原來segment里面才是真正的hashtable,即每個segment是一個傳統意義上的hashtable, 從兩者的結構就可以看出區別,這里就是找出需要的entry在table的哪一個位置,之后得到的entry就是這個鏈的第一個節點,如果e!=null,說明找到了,這是就要替換節點的值(onlyIfAbsent == false),否則,我們需要new一個entry,它的后繼是first,而讓tab[index]指向它,什么意思呢?實際上就是將這個新entry 插入到鏈頭,剩下的就非常容易理解了,
get 方法(請注意,這里分析的方法都是針對桶的,因為ConcurrentHashMap的最大改進就是將粒度細化到了桶上),首先判斷了當前桶的資料個數是 否為0,為0自然不可能get到什么,只有回傳null,這樣做避免了不必要的搜索,也用最小的代價避免出錯,然后得到頭節點(方法將在下面涉及)之后就 是根據hash和key逐個判斷是否是指定的值,如果是并且值非空就說明找到了,直接回傳;
程式非常簡單,但有一個令人困惑的地方,這句return readValueUnderLock(e)到底是用來干什么的呢?研究它的代碼,在鎖定之后回傳一個值,但這里已經有一句V v = e.value得到了節點的值,這句return readValueUnderLock(e)是否多此一舉?
事實上,這里完全是為了并發考慮的,這里當v為空時,可能是一個執行緒正在改變節點,而之前的 get操作都未進行鎖定,根據bernstein條件,讀后寫或寫后讀都會引起資料的不一致,所以這里要對這個e重新上鎖再讀一遍,以保證得到的是正確值,
這里不得不佩服Doug Lea思維的嚴密性,整個get操作只有很少的情況會鎖定,相對于之前的Hashtable,并發是不可避免的啊!
ConcurrentHashmap只能保證自身資料在多執行緒的環境下不被破壞,而并不能保證業務邏輯的正確性,
ConcurrentHashMap的鎖是讀鎖
3.7、 sleep()和wait()分別是哪個類的方法,有什么區別?synchronized底層如何實作的?用在代碼塊和方法上有什么區別?
sleep方法是Thread類的靜態方法,呼叫此方法會讓當前執行緒暫停指定的時間,將執行機會(CPU)讓給其他執行緒,但是不會釋放鎖,因此休眠時間結束后自動恢復(程式回到就緒狀態),
wait是Object類的方法,呼叫物件的wait方法導致執行緒放棄CPU的執行權,同時也放棄物件的鎖(執行緒暫停執行),進入物件的等待池(wait pool),只有呼叫物件的notify或notifyAll方法才能喚醒等待池中的執行緒進入等鎖池(lock pool),如果執行緒重新獲得物件的鎖就可以進入就緒狀態,
wait只能在同步控制方法中或者同步控制塊中使用,而sleep可以在任何地方使用,
wait 可以指定時間也可以不指定,指定時間 wait(time) 在 time時間內 有別的執行緒 notifyAll() 是不會喚醒到它 ,sleep 必須指定時間
synchronized代碼塊是由一對monitorenter/monitorexit指令實作的, Monitor物件是同步的基本實作單元,
現代的(Oracle) JDK6中, JVM對此進行了大刀闊斧地改進,提供了三種不同的Monitor實作,也就是常說的三種不同的鎖:
偏斜鎖(Biased Locking)、輕量級鎖和重量級鎖,大大改進了其性能,
同步方法直接在方法上加synchronized實作加鎖,同步代碼塊則在方法內部加鎖,很明顯,同步方法鎖的范圍比較大,而同步代碼塊范圍要小點,
一般同步的范圍越大,性能就越差,一般需要加鎖進行同步的時候,肯定是范圍越小越好,這樣性能更好,
3.8、 什么是執行緒池?如果讓你設計一個動態大小的執行緒池,如何設計,應該有哪些方法?
執行緒池就是創建若干個可執行的執行緒放入一個池(容器)中,有任務需要處理時,會提交到執行緒池中的任務佇列,處理完之后執行緒并不會被銷毀,而是仍然在執行緒池中等待下一個任務,
一個執行緒池包括以下四個基本組成部分:
執行緒管理器 (ThreadPool): 用于創建并管理執行緒池,包括創建執行緒,銷毀執行緒池,添加新任務;
作業執行緒 (PoolWorker): 執行緒池中執行緒,在沒有任務時處于等待狀態,可以回圈的執行任務;
任務介面 (Task): 每個任務必須實作的介面,以供作業執行緒調度任務的執行,它主要規定了任務的入口,任務執行完后的收尾作業,任務的執行狀態等;
任務佇列 (TaskQueue): 用于存放沒有處理的任務,提供一種緩沖機制;
所包含的方法
//創建執行緒池
private ThreadPool()
//獲得一個默認執行緒個數的執行緒池
public static ThreadPool getThreadPool()
//執行任務,其實只是把任務加入任務佇列,什么時候執行有執行緒池管理器決定
public void execute(Runnable task)
//批量執行任務,其實只是把任務加入任務佇列,什么時候執行有執行緒池管理器決定
public void execute(Runnable[] task)
//銷毀執行緒池,該方法保證在所有任務都完成的情況下才銷毀所有執行緒,否則等待任務完成才銷毀
public void destroy()
//回傳作業執行緒的個數
public int getWorkThreadNumber()
//回傳已完成任務的個數,這里的已完成是只出了任務佇列的任務個數,可能該任務并沒有實際執行完成
public int getFinishedTasknumber()
//在保證執行緒池中所有執行緒正在執行,并且要執行執行緒的個數大于某一值時,增加執行緒池中執行緒的個數
public void addThread()
//在保證執行緒池中有很大一部分執行緒處于空閑狀態,并且空閑狀態的執行緒在小于某一值時,減少執行緒池中執行緒的個數
public void reduceThread()
3.9、 什么是死鎖?JVM執行緒死鎖,你該如何判斷是因為什么?如果用VisualVM,dump執行緒資訊出來,會有哪些資訊?
(不懂還是不懂,??)
執行緒死鎖是指由于兩個或者多個執行緒互相持有對方所需要的資源,導致這些執行緒處于等待狀態,無法前往執行,
常常需要在隔兩分鐘后再次收集一次thread dump,如果得到的輸出相同,仍然是大量thread都在等待給同一個 地址上鎖,那么肯定是死鎖了,
3.10、 查看jvm虛擬機里面堆、執行緒的資訊,你用過什么命令?
-heap : 列印jvm heap的情況,會列出堆的總體使用情況,還有新生代老生代的記憶體占用情況,
-histo: 列印jvm heap的直方圖,其輸出資訊包括類名,物件數量,物件占用大小,
-histo:live : 同上,但是只答應存活物件的情況
-permstat: 列印permanent generation heap情況
等等
3.11、 垃圾回收演算法有哪些?CMS知道嗎?如何作業的?
標記-清除演算法
從演算法的名稱上可以看出,這個演算法分為兩部分,標記和清除,首先標記出所有需要被回收的物件,然后在標記完成后統一回收掉所有被標記的物件,
這個演算法簡單,但是有兩個缺點:一是標記和清除的效率不是很高;二是標記和清除后會產生很多的記憶體碎片,導致可用的記憶體空間不連續,當分配大物件的時候,沒有足夠的空間時不得不提前觸發一次垃圾回收,
復制演算法
這個演算法將可用的記憶體空間分為大小相等的兩塊,每次只是用其中的一塊,當這一塊被用完的時候,就將還存活的物件復制到另一塊中,然后把原已使用過的那一塊記憶體空間一次回收掉,這個演算法常用于新生代的垃圾回收,
復制演算法解決了標記-清除演算法的效率問題,以空間換時間,但是當存活物件非常多的時候,復制操作效率將會變低,而且每次只能使用一半的記憶體空間,利用率不高,
標記-整理演算法
這個演算法分為三部分:
一是標記出所有需要被回收的物件;
二是把所有存活的物件都向一端移動;三是把所有存活物件邊界以外的記憶體空間都回收掉,
標記-整理演算法解決了復制演算法多復制效率低、空間利用率低的問題,同時也解決了記憶體碎片的問題,
分代收集演算法
根據物件生存周期的不同將記憶體空間劃分為不同的塊,然后對不同的塊使用不同的回收演算法,一般把Java堆分為新生代和老年代,新生代中物件的存活周期短,只有少量存活的物件,所以可以使用復制演算法,而老年代中物件存活時間長,而且物件比較多,所以可以采用標記-清除和標記-整理演算法,
CMS作業程序
初始標記 :在這個階段,需要虛擬機停頓正在執行的任務,官方的叫法STW(Stop The Word),這個程序從垃圾回收的"根物件"開始,只掃描到能夠和"根物件"直接關聯的物件,并作標記,所以這個程序雖然暫停了整個JVM,但是很快就完成了,
并發標記 :這個階段緊隨初始標記階段,在初始標記的基礎上繼續向下追溯標記,并發標記階段,應用程式的執行緒和并發標記的執行緒并發執行,所以用戶不會感受到停頓,
并發預清理 :
并發預清理階段仍然是并發的,在這個階段,虛擬機查找在執行并發標記階段新進入老年代的物件(可能會有一些物件從新生代晉升到老年代, 或者有一些物件被分配到老年代),通過重新掃描,減少下一個階段"重新標記"的作業,因為下一個階段會Stop The World,
重新標記 : 這個階段會暫停虛擬機,收集器執行緒掃描在CMS堆中剩余的物件,掃描從"跟物件"開始向下追溯,并處理物件關聯,
并發清理 : 清理垃圾物件,這個階段收集器執行緒和應用程式執行緒并發執行,
并發重置 : 這個階段,重置CMS收集器的資料結構,等待下一次垃圾回收,
3.12、 資料庫中什么是事務?事務的隔離級別?事務的四個特性?什么是臟讀,幻讀,不可重復讀?
事務(transaction)是作為一個單元的一組有序的資料庫操作,
如果組中的所有操作都成功,則認為事務成功,即使只有一個操作失敗,事務也不成功,如果所有操作完成,事務則提交
資料庫事務的隔離級別由低到高分別為
Read uncommitted 、Read committed(大多數資料庫) 、Repeatable read(Sql Server , Oracle,Mysql) 、Serializable
事務的四個特性:
1 、原子性
事務是資料庫的邏輯作業單位,事務中包含的各操作要么都做,要么都不做
2 、一致性
(只有一種結果,不是全寫入資料庫,就是全都沒寫入資料庫)
事 務執行的結果必須是使資料庫從一個一致性狀態變到另一個一致性狀態,因此當資料庫只包含成功事務提交的結果時,就說資料庫處于一致性狀態,如果資料庫系統 運行中發生故障,有些事務尚未完成就被迫中斷,這些未完成事務對資料庫所做的修改有一部分已寫入物理資料庫,這時資料庫就處于一種不正確的狀態,或者說是 不一致的狀態,
3 、隔離性
一個事務的執行不能其它事務干擾,即一個事務內部的操作及使用的資料對其它并發事務是隔離的,并發執行的各個事務之間不能互相干擾,
4 、持續性
也稱永久性,指一個事務一旦提交,它對資料庫中的資料的改變就應該是永久性的,接下來的其它操作或故障不應該對其執行結果有任何影響,
臟讀 是指在一個事務處理程序里讀取了另一個未提交的事務中的資料,
幻讀 是事務非獨立執行時發生的一種現象,例如事務T1對一個表中所有的行的某個資料項做了從“1”修改為“2”的操作,這時事務T2又對這個表中插入了一行資料項,而這個資料項的數值還是為“1”并且提交給資料庫,而操作事務T1的用戶如果再查看剛剛修改的資料,會發現還有一行沒有修改,其實這行是從事務T2中添加的,就好像產生幻覺一樣,這就是發生了幻讀,
不可重復讀 是指在對于資料庫中的某個資料,一個事務范圍內多次查詢卻回傳了不同的資料值,這是由于在查詢間隔,被另一個事務修改并提交了,
3.13、 資料庫索引的結構有哪些? 介紹B+樹的結構,
哈希表和有序陣列
二叉樹和多叉樹
B+樹
1.根結點至少有兩個子女,
2.每個中間節點都至少包含ceil(m / 2)個孩子,最多有m個孩子,
3.每一個葉子節點都包含k-1個元素,其中 m/2 <= k <= m,
4.所有的葉子結點都位于同一層,
5.每個節點中的元素從小到大排列,節點當中k-1個元素正好是k個孩子包含的元素的值域分劃,
3.14、 資料庫中的分頁查詢陳述句怎么寫?
select * from table limit (start-1)*limit,limit; --其中start是頁碼,limit是每頁顯示的條數,
1
3.15、 什么是一致性哈希?用來解決什么問題?
一致性Hash演算法將整個哈希值空間組織成一個虛擬的圓環,整個空間按順時針方向組織
圓環的正上方的點代表0,0點右側的第一個點代表1,以此類推,
0點的左側是2的32次方-1,把這個圓環叫做Hash環
然后把服務器ip或者主機名字作為關鍵字Hash,每個服務器都能確定位置,把資料進行相同的Hash算出的位置,順時針訪問的第一個就是對應的服務器
一致性Hash演算法對于節點的增減都只需重定位環空間中的一小部分資料,具有較好的容錯性和可擴展性,
用來解決的問題:
hash( key ) % N,N 為 Redis 的數量,在這里 N = 4 ;
4 臺 Redis 不夠了,需要再增加 4 臺 Redis ;那么這個求余演算法就會變成:hash( key ) % 8 ;
一部分%4一部分%8當前大部分快取的位置都會是錯誤的,極端情況下,就會造成 快取雪崩,
(如果節點太少或分布不均勻的時候,容易造成 資料傾斜,也就是大部分資料會集中在某一臺服務器上,,一致性 Hash 演算法提出了【虛擬節點】解決資料傾斜問題)
3.16、 Redis的存盤結構,或者說如何作業的,與mysql的區別?有哪些資料型別?
redis中以key-value的形式存盤,key固定是字串,使用字串物件進行表示,value可以是字串(String)、串列(List)、哈希(Hash)、集合(Set)、有序集合(ZSet),
redis和mysql的區別總結:
(1)型別上
從型別上來說,mysql是關系型資料庫,redis是快取資料庫
(2)作用上
mysql用于持久化的存盤資料到硬碟,功能強大,速度較慢,基于磁盤,讀寫速度沒有Redis快,但是不受空間容量限制,性價比高
redis用于存盤使用較為頻繁的資料到快取中,讀取速度快,基于記憶體,讀寫速度快,也可做持久化,但是記憶體空間有限,當資料量超過記憶體空間時,需擴充記憶體,但記憶體價格貴
(3)需求上
mysql和redis因為需求的不同,一般都是配合使用,
需要高性能的地方使用Redis,不需要高性能的地方使用MySQL,存盤資料在MySQL和Redis之間做同步,
3.17、 專案中用到redis,為什么選用redis,了解其他NoSQL資料庫嗎?在你的專案中是如何運用redis的?key是什么,value是什么?
(又是涼涼的一道題,??)
為什么選用redis
高效性: Redis讀取的速度是110000次/s,寫的速度是81000次/s
原子性: Redis的所有操作都是原子性的,同時Redis還支持對幾個操作全并后的原子性執行,
支持多種資料結構: string(字串);list(串列);hash(哈希),set(集合);zset(有序集合)
穩定性: 持久化,主從復制(集群)
其他特性: 支持過期時間,支持事務,訊息訂閱,
其他NoSQL資料庫:
memcache介紹
很早出現的NoSql資料庫資料都在記憶體中,一般不持久化支持簡單的key-value模式一般是作為快取資料庫輔助持久化的資料庫
mongoDB介紹
高性能、開源、模式自由(schema free)的檔案型資料庫資料都在記憶體中, 如果記憶體不足,把不常用的資料保存到硬碟雖然是key-value模式,但是對value(尤其是json)提供了豐富的查詢功能支持二進制資料及大型物件可以根據資料的特點替代RDBMS(關系資料庫管理系統) ,成為獨立的資料庫,或者配合RDBMS,存盤特定的資料,
列式存盤HBase介紹
HBase是Hadoop專案中的資料庫,它用于需要對大量的資料進行隨機、實時的讀寫操作的場景中,HBase的目標就是處理資料量非常龐大的表,可以用普通的計算機處理超過10億行資料,還可處理有數百萬列元素的資料表,
如何運用redis
冷熱資料區分
雖然 Redis支持持久化,但將所有資料存盤在 Redis 中,成本非常昂貴,建議將熱資料 (如 QPS超過 5k) 的資料加載到 Redis 中,低頻資料可存盤在 Mysql、 ElasticSearch中,
業務資料分離
不要將不相關的資料業務都放到一個 Redis中,一方面避免業務相互影響,另一方面避免單實體膨脹,并能在故障時降低影響面,快速恢復,
訊息大小限制
由于 Redis 是單執行緒服務,訊息過大會阻塞并拖慢其他操作,保持訊息內容在 1KB 以下是個好的習慣,嚴禁超過 50KB 的單條記錄,訊息過大還會引起網路帶寬的高占用,持久化到磁盤時的 IO 問題,
連接數限制
連接的頻繁創建和銷毀,會浪費大量的系統資源,極限情況會造成宿主機當機,請確保使用了正確的 Redis 客戶端連接池配置,
快取 Key 設定失效時間
作為快取使用的 Key,必須要設定失效時間,失效時間并不是越長越好,請根據業務性質進行設定,注意,失效時間的單位有的是秒,有的是毫秒,這個很多同學不注意容易搞錯,
快取不能有中間態
快取應該僅作快取用,去掉后業務邏輯不應發生改變,萬不可切入到業務里,
快取的高可用會影響業務;
產生深耦合會發生無法預料的效果;
會對維護行產生膚效果,
擴展方式首選客戶端 hash
如果應用太小就別考慮了,如單 redis 集群并不能為你的資料服務,不要著急擴大你的 redis 集群(包括 M/S 和 Cluster),集群越大,在狀態同步和持久化方面的性能越差,優先使用客戶端 hash 進行集群拆分,如:根據用戶 id 分 10 個集群,用戶尾號為 0 的落在第一個集群,
操作限制
嚴禁使用 Keys
Keys 命令效率極低,屬于 O(N)操作,會阻塞其他正常命令,在 cluster 上,會是災難性的操作,嚴禁使用,DBA 應該 rename 此命令,從根源禁用,
嚴禁使用 Flush
flush 命令會清空所有資料,屬于高危操作,嚴禁使用,DBA 應該 rename 此命令,從根源禁用,僅 DBA 可操作,
嚴禁作為訊息佇列使用
如沒有非常特殊的需求,嚴禁將 Redis 當作訊息佇列使用,Redis 當作訊息佇列使用,會有容量、網路、效率、功能方面的多種問題,如需要訊息佇列,可使用高吞吐的 Kafka 或者高可靠的 RocketMQ,
嚴禁不設定范圍的批量操作
redis 那么快,慢查詢除了網路延遲,就屬于這些批量操作函式,大多數線上問題都是由于這些函式引起,
1、[zset] 嚴禁對 zset 的不設范圍操作
ZRANGE、 ZRANGEBYSCORE等多個操作 ZSET 的函式,嚴禁使用 ZRANGE myzset 0 -1 等這種不設定范圍的操作,請指定范圍,如 ZRANGE myzset 0 100,如不確定長度,可使用 ZCARD 判斷長度
2、[hash] 嚴禁對大資料量 Key 使用 HGETALL
HGETALL會取出相關 HASH 的所有資料,如果資料條數過大,同樣會引起阻塞,請確保業務可控,如不確定長度,可使用 HLEN 先判斷長度
3、[key] Redis Cluster 集群的 mget 操作
Redis Cluster 的 MGET 操作,會到各分片取資料聚合,相比傳統的 M/S架構,性能會下降很多,請提前壓測和評估
4、[其他] 嚴禁使用 sunion, sinter, sdiff等一些聚合操作
禁用 select 函式
select函式用來切換 database,對于使用方來說,這是很容易發生問題的地方,cluster 模式也不支持多個 database,且沒有任何收益,禁用,
禁用事務
redis 本身已經很快了,如無大的必要,建議捕獲例外進行回滾,不要使用事務函式,很少有人這么干,
禁用 lua 腳本擴展
lua 腳本雖然能做很多看起來很 cool 的事情,但它就像是 SQL 的存盤程序,會引入性能和一些難以維護的問題,禁用,
禁止長時間 monitor
monitor函式可以快速看到當前 redis 正在執行的資料流,但是當心,高峰期長時間阻塞在 monitor 命令上,會嚴重影響 redis 的性能,此命令不禁止使用,但使用一定要特別特別注意,
Key 規范
Redis 的 Key 一定要規范,這樣在遇到問題時,能夠進行方便的定位,Redis 屬于無 scheme 的 KV 資料庫,所以,我們靠約定來建立其 scheme 語意,
redis中以key-value的形式存盤,key固定是字串,使用字串物件進行表示,value可以是字串(String)、串列(List)、哈希(Hash)、集合(Set)、有序集合(ZSet),
3.18、 歸并排序的程序?時間復雜度?空間復雜度?你平常用什么排序?快速排序,說說在那些場景下適用,哪些場景下不適用,
這個就不總結了,大家面試之前要把排序的幾大演算法認真看看
3.19、 Solr是如何作業的?
solr是基于Lucence開發的企業級搜索引擎技術,而lucence的原理是倒排索引,
倒排索引:
Demo1:A在吃飯
Demo2:B在睡覺
記錄就變成了這樣
關鍵詞 段落號(出現頻率) 出現位置
吃飯 1(1) 3
在 1(1) 2
2(1) 2
A 1(1) 1
睡覺 2(1) 3
B 2(1) 1
整理不易,有描述的不對的地方還望大佬賜教
面試題整理,需要的可以【點擊這里,暗號博客園!!】

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/261982.html
標籤:其他
上一篇:《你好,李煥英》票房破39億,用Python分析一下這部春節檔的最大黑馬
下一篇:零基礎學Python:編程規范
