史上最全的中高階JAVA工程師-面試題彙總

Jeff.Smile發表於2019-10-15

瞭解博主本人,請閱讀《成就一億技術人,我在CSDN的這九年》

文章目錄

快取

memcache的分散式原理

memcached 雖然稱為 “ 分散式 ” 快取伺服器,但伺服器端並沒有 “ 分散式 ” 功能。每個伺服器都是完全獨立和隔離的服務。 memcached 的分散式,則是完全由客戶端程式庫實現的。 這種分散式是 memcached 的最大特點。

memcache的記憶體分配機制

如何存放資料到memcached快取中?(memcache記憶體分配機制)

Slab Allocator記憶體分配機制
預先將記憶體分配成數個slab倉庫,每個倉庫再切出不同大小的chunk,去適配收到的資料。多餘的只能造成浪費,不可避免。
增長因子(Grace factor):一般而言觀察資料大小的變化規律設定合理的增長因子,預設1.25倍.
太大容易造成浪費。memcached.exe -m 64 -p 11211 -f 1.25

如果有100byte的內容要儲存,但122大小的倉庫的chunk用滿了怎麼辦?
答:是並不會尋找更大倉庫的chunk來儲存,而是把122倉庫中的舊資料踢掉!

memcache的惰性失效機制

1 當某個值過期後並不會從記憶體刪除。(因此status統計時的curr_items有其資訊)
2 如果之前沒有get過,將不會自動刪除。
如果(過期失效,沒get過一次)又沒有一個新值去佔用他的位置時,當做空的chunk佔用。
3 當取其值(get)時,判斷是否過期:如果過期返回空,且清空。(所以curr_items就減少了)
即這個過期只是讓使用者看不到這個資料而已,並沒有在過期的瞬間立即從記憶體刪除,這個過程
稱為lazy expirtion,屬性失效,好處是節約了cpu和檢測的成本,稱為“惰性失效機制”

memcache快取的無底洞現象

快取的無底洞現象
facebook的工作人員反應,他們在2010年左右,memcacahed節點就已經達到3000個,大約數千G的快取,他們發現一個問題,memchache連線頻率太高導致效率下降,於是加memcache節點,新增後發現連線頻率導致的問題仍然沒有好轉,稱之為“無底洞現象”。

問題分析
以使用者為例:user-133-age,user-133_name,user-133-height…N個key
當伺服器增多,133號使用者的資訊也被散落在更多的伺服器,
所以同樣是訪問個人主頁,得到相同的個人資訊,節點越多,要連線節點越多,對於memcache的連線數並沒有隨著節點的增多而降低,問題出現。

事實上
nosql和傳統的rdbms並不是水火不容,兩者在某些設計上是可以相互參考的。
對於nosql的key-value這種儲存,key的設計可以參考mysql中表和列的設計。
比如user表下有age、name、height列,對應的key可以用user:133:age=23,user:133:name=ls,user:133:height=168;

問題的解決方案
把某一組key按其共同字首來分佈,比如:user:133:age=23,user:133:name=ls,user:133:height=168;
在用分散式演算法求其節點時,應該以user:133來計算,而不是以user:133:age來計算,這樣這三個關於個人資訊的key都落在同一個節點上。
再次訪問只需要連線一個節點。問題解決。

一致性Hash演算法的實現原理

Hash環

我們把232次方想成一個環,比如鐘錶上有60個分針點組成一個圓,那麼hash環就是由232個點組成的圓。第一個點是0,最後一個點是232-1,我們把這232個點組成的環稱之為HASH環。
在這裡插入圖片描述

一致性Hash演算法

將memcached物理機節點通過Hash演算法虛擬到一個虛擬閉環上(由0到232構成),key請求的時候通過Hash演算法計算出Hash值然後對232取模,定位到環上順時針方向最接近的虛擬物理節點就是要找到的快取伺服器。

假設有ABC三臺快取伺服器:
我們使用這三臺伺服器各自的IP進行hash計算然後對2~32取模即:
           Hash(伺服器IP)%2~32
計算出來的結果是0到2~32-1的一個整數,那麼Hash環上必有一個點與之對應。比如:
在這裡插入圖片描述
在這裡插入圖片描述
現在快取伺服器已經落到了Hash環上,接下來我們就看我們的資料是怎麼放到快取伺服器的?
我們可以同樣對Object取Hash值然後對2~32取模,比如落到了接近A的一個點上:
在這裡插入圖片描述
那麼這個資料理應存到A這個快取伺服器節點上
在這裡插入圖片描述
所以,在快取伺服器節點數量不變的情況下,快取的落點是不會變的。
在這裡插入圖片描述
但是如果B掛掉了呢?
按照hash且取模的演算法,圖中3這個Object理應就分配到了C這個節點上去了,所以就會到C上找快取資料,結果當然是找不到,進而從DB讀取資料重新放到了C上。
在這裡插入圖片描述
但是對於編號為1,2的Object還是落到A,編號為4的Object還是落到C,B當機所影響的僅僅是3這個Object。這就是一致性Hash演算法的優點。

Hash環的傾斜

前面我們理想化的把三臺memcache機器均勻分到了Hash環上:
在這裡插入圖片描述
但是現實情況可能是:
在這裡插入圖片描述
如果Hash環傾斜,即快取伺服器過於集中將會導致大量快取資料被分配到了同一個伺服器上。比如編號1,2,3,4,6的Object都被存到了A,5被存到B,而C上竟然一個資料都沒有,這將造成記憶體空間的浪費。
為了解決這個問題,一致性Hash演算法中使用“虛擬節點”解決。
在這裡插入圖片描述

虛擬節點解決Hash環傾斜

在這裡插入圖片描述
“虛擬節點”是“實際節點”在hash環上的複製品,一個實際節點可能對應多個虛擬節點。這樣就可以將ABC三臺伺服器相對均勻分配到Hash環上,以減少Hash環傾斜的影響,使得快取被均勻分配到hash環上。

hash演算法平衡性

  平衡性指的是hash的結果儘可能分佈到所有的快取中去,這樣可以使得所有的快取空間都可以得到利用。但是hash演算法不保證絕對的平衡性,為了解決這個問題一致性hash引入了“虛擬節點”的概念。虛擬節點”( virtual node )是實際節點在 hash 空間的複製品( replica ),一實際個節點對應了若干個“虛擬節點”,這個對應個數也成為“複製個數”,“虛擬節點”在 hash 空間中以 hash 值排列。“虛擬節點”的hash計算可以採用對應節點的IP地址加數字字尾的方式。
  例如假設 cache A 的 IP 地址為202.168.14.241 。
  引入“虛擬節點”前,計算 cache A 的 hash 值: Hash(“202.168.14.241”);
  引入“虛擬節點”後,計算“虛擬節”點 cache A1 和 cache A2 的 hash 值:
    Hash(“202.168.14.241#1”); // cache A1
    Hash(“202.168.14.241#2”); // cache A2
  這樣只要是命中cacheA1和cacheA2節點,就相當於命中了cacheA的快取。這樣平衡性就得到了提高。
  參考:https://www.cnblogs.com/yixiwenwen/p/3580646.html

memcached與redis的區別

1 redis做儲存,可以持久化,memcache做快取,資料易丟失。
2 redis支援多資料型別,memcache存放字串。
3 redis服務端僅支援單程式、單執行緒訪問,也就是先來後到的序列模式,避免執行緒上下文切換,自然也就保證資料操作的原子性。Memcache服務端是支援多執行緒訪問的。
4 redis雖然是單程式單執行緒模式,但是redis使用了IO多路複用技術做到一個執行緒可以處理很多個請求來保證高效能。

Redis的主從複製

1 在Slave啟動並連線到Master之後,它將主動傳送一個SYNC命令給Master。
2 Master在收到SYNC命令之後,將執行BGSAVE命令執行後臺存檔程式(rdb快照), 同時收集所有接收到的修改資料集的命令即寫命令到緩衝區,在後臺存檔程式執行完畢後,Master將傳送整個資料庫檔案到Slave。
3 Slave在接收到資料庫檔案資料之後,將自身記憶體清空,載入rdb檔案到記憶體中完成一次完全同步。
4 接著,Master繼續將所有已經收集到緩衝區的修改命令,和新的修改命令依次傳送給Slaves
5 Slave將在本地執行這些資料修改命令,從而達到最終的資料同步
6 之後Master和Slave之間會不斷通過非同步方式進行命令的同步,從而保證資料的實時同步
7 如果Master和Slave之間的連結出現斷連現象,Slave可以自動重連Master,但是在
重新連線成功之後:
2.8之前的redis將進行一次完全同步
2.8之後進行部分同步,使用的是PSYNC命令
如下:

Redis的部分複製過程

部分同步工作原理如下:
1):Master為被髮送的複製流建立一個記憶體緩衝區(in-memory backlog),記錄最近傳送的複製流命令
2):Master和Slave之間都記錄一個複製偏移量(replication offset)和當前Master ID(Master run id)
3):當出現網路斷開,Slave會重新連線,並且向Master請求繼續執行原來的複製程式
4):如果Slave中斷網前的MasterID和當前要連的MasterID相同,並且從斷開時到當前時刻Slave記錄的偏移量所指定的資料仍然儲存在Master的複製流緩衝區裡面,則Master會向Slave傳送缺失的那部分資料,Slave執行後複製工作可以繼續執行。
5):否則Slave就執行完整重同步操作

Redis的主從複製阻塞模式

1 同一個Master服務可以同步n多個Slave服務
2 Slave節點同樣可以接受其它Slave節點的連線和同步服務請求,分擔Master節點的同步壓力
3 Master是以非阻塞方式為Slave提供同步服務,所以主從複製期間Master一樣可以提供讀寫請求。
4 Slave同樣是以非阻塞的方式完成資料同步,在同步期間,如果有客戶端提交查詢請求,Redis則返回同步之前的資料

Redis的資料持久化方式

Rdb快照和aof
RDB快照:可以配置在n秒內有m個key修改就做自動化快照方式
AOF:每一個收到的寫命令都通過write函式追加到檔案中。更安全。

Redis的高可用部署方式

哨兵模式

redis3.0之前的Sentinel哨兵機制,redis3.0之前只能使用一致性hash方式做分散式快取。哨兵的出現主要是解決了主從複製出現故障時需要人為干預的問題。

Redis哨兵主要功能

(1)叢集監控:負責監控Redis master和slave程式是否正常工作
(2)訊息通知:如果某個Redis例項有故障,那麼哨兵負責傳送訊息作為報警通知給管理員
(3)故障轉移:如果master node掛掉了,會自動轉移到slave node上
(4)配置中心:如果故障轉移發生了,通知client客戶端新的master地址

Redis哨兵的高可用

原理:當主節點出現故障時,由Redis Sentinel自動完成故障發現和轉移,並通知應用方,實現高可用性
在這裡插入圖片描述

哨兵機制建立了多哨兵節點,共同監控資料節點的執行狀況。
同時哨兵節點之間也互相通訊,交換對主從節點的監控狀況。
每隔1秒每個哨兵會向整個叢集:Master主伺服器+Slave從伺服器+其他Sentinel(哨兵)程式,傳送一次ping命令做一次心跳檢測。

哨兵如何判斷redis主從節點是否正常?

涉及兩個新的概念:主觀下線和客觀下線。

  1. 主觀下線:一個哨兵節點判定主節點down掉是主觀下線。
  2. 客觀下線:只有半數哨兵節點都主觀判定主節點down掉,此時多個哨兵節點交換主觀判定結果,才會判定主節點客觀下線。

原理:基本上哪個哨兵節點最先判斷出這個主節點客觀下線,就會在各個哨兵節點中發起投票機制Raft演算法(選舉演算法),最終被投為領導者的哨兵節點完成主從自動化切換的過程。

叢集模式

  redis3.0之後的容錯叢集方式,無中心結構,每個節點儲存資料和整個叢集狀態,每個節點都和其他所有節點連線,需要至少三個master提供寫的功能。
  因此叢集中至少應該有奇數個節點,因此至少有三個節點,每個節點至少有一個備份節點,所以redis叢集應該至少6個節點。
  每個Master有一個範圍的slot槽位用於寫資料。

Redis可以線上擴容嗎?zk呢

  Reids的線上擴容,不需要重啟伺服器,動態的在原始叢集中新增新的節點,並分配slot槽。
但是zk不能線上擴容,需要重啟,但是我們可以選擇一個一個重啟。

Redis高併發和快速的原因

1.redis是基於記憶體的,記憶體的讀寫速度非常快;
2.redis是單執行緒的,省去了很多上下文切換執行緒的時間;
3.redis使用多路複用技術,可以處理併發的連線。
缺點:無法發揮多核CPU效能

瀏覽器本地快取的瞭解和使用

資源在瀏覽器端的本地快取可以通過Expires和Last-Modified返回頭資訊進行有效控制。

1)Expires告訴瀏覽器在該指定過期時間前再次訪問同一URL時,直接從本地快取讀取,無需再向伺服器發起http請求;
  優點是:瀏覽器直接讀取快取資訊無需發起http請求。
   缺點是:當使用者按F5或Ctl+F5重新整理頁面時瀏覽器會再次發起http請求。

2)當伺服器返回設定了Last-Modified頭,下次發起同一URL的請求時,請求頭會自動包含If-Modified-Since頭資訊,伺服器對靜態內容會根據該資訊跟檔案的最後修改時間做比較,如果最後修改時間不大於If-Modified-Since頭資訊,則返回304:告訴瀏覽器請求內容未更新可直接使用本地快取。
(注意:只對靜態內容有效,如js/css/image/html等,不包括動態內容,如JSP)
優點:無論使用者行為如何都有效;
缺點:仍需向伺服器發起一次http請求;

快取雪崩

如果快取集中在一段時間內失效,發生大量的快取穿透,所有的查詢都落在資料庫上,造成了快取雪崩。

解決辦法:
沒有完美的解決方案,可以通過隨機演算法讓失效時間隨機分佈,避免同一時刻失效。

快取穿透

訪問一個不存在的key,快取不起作用,請求會穿透到DB,可能DB也沒查到,流量大時DB會掛掉。

解決辦法:
1.採用布隆過濾器,使用一個足夠大的bitmap,用於儲存可能訪問的key,不存在的key直接被過濾;
2訪問key未在DB查詢到值,也將空值寫進快取,但可以設定較短過期時間。

HashMap

HashMap的Hash碰撞

在這裡插入圖片描述
  Hash值衝突問題是Hash表儲存模型需要解決的一個問題。通常有兩種方法:
  將相同Hash值的Entry物件組織成一個連結串列放置在hash值對應的槽位。HashMap採用的是連結串列法,且是單向連結串列(通過head元素就可以操作後續所有元素,對連結串列而言,新加入的節點會從頭節點加入。)
核心原始碼:

private void addEntry(int hash, K key, V value, int bucketIndex) {  
    Entry<K,V> e = table[bucketIndex];  
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e);  
    if (size++ >= threshold)  
        resize(2 * table.length);  
}

以上程式碼說明:
系統總是將新新增的 Entry 物件放入 table 陣列的 bucketIndex 索引處。
1 如果 bucketIndex 索引處已經有了一個 Entry 物件,那新新增的 Entry 物件指向原有的 Entry 物件
(產生一個 Entry 鏈)
2 如果 bucketIndex 索引處沒有 Entry 物件,也就是上面程式程式碼的 e 變數是 null,也就是新放入的
Entry 物件指向 null,也就是沒有產生 Entry 鏈。
HashMap裡面沒有出現hash衝突時,沒有形成單連結串列時,hashmap查詢元素很快,get()方法能夠直接定位到元素,
但是出現單連結串列後,單個bucket 裡儲存的不是一個 Entry,而是一個 Entry 鏈,系統只能必須按順序遍歷每個
Entry,直到找到想搜尋的 Entry 為止——如果恰好要搜尋的 Entry 位於該 Entry 鏈的最末端(該 Entry 是最早
放入該 bucket 中),那系統必須迴圈到最後才能找到該元素。

HashMap的get和put原理

PUT原理
當呼叫HashMap的put方法傳遞key和value時,先呼叫key的hashcode方法。
通過key的Hash值來找到Bucket----‘桶’的位置,然後迭代這個位置的Entry列表
判斷是否存在key的hashcode和equals完全相同的key,如果完全相同則覆蓋value,
否則插入到entry鏈的頭部。

HashMap在put時的Entry鍊形成的場景

當程式試圖將一個key-value對放入HashMap中時,程式首先根據該 key 的 hashCode() 返回值決定該 Entry 的儲存位置:
如果這兩個 Entry 的 key 的 hashCode() 返回值相同,那它們的儲存位置相同。
如果這兩個 Entry 的 key 通過 equals 比較返回 true,新新增 Entry 的 value 將覆蓋集合中原有 Entry 的 value,但key不會覆蓋。

如果這兩個 Entry 的 key 通過 equals 比較返回 false,新新增的 Entry 將與集合中原有 Entry 形成 Entry 鏈,而且新新增的 Entry 位於 Entry 鏈的頭部

GET原理
根據該 key 的 hashCode 值計算它的 hash 碼,遍歷並迴圈取出 Entry 陣列中指定索引處的Entry值,如果該 Entry 的 key 與被搜尋 key 相同 ,且Enrty的hash值跟key的hash碼相同,然後看是否是Entry鏈,如果是則迭代這個位置的Entry列表,判斷是否存在key的hashcode和equals完全相同的key,如果完全相同則獲取value。

HashMap的rehash

  HashMap初始容量大小為16,一般來說,當有資料要插入時,都會檢查容量有沒有超過設定的thredhold,如果超過,需要增大Hash表的尺寸,但是這樣一來,整個Hash表裡的元素都需要被重算一遍。這叫rehash,這個成本相當的大

HashMap的執行緒不安全問題

  比如put操作時,有兩個執行緒A和B,首先A希望插入一個key-value對到HashMap中,首先計算記錄所要落到的桶的索引BucketIndex座標,然後獲取到該桶裡面的Entry連結串列header頭結點,此時執行緒A的時間片用完了,而此時執行緒B被排程得以執行,和執行緒A一樣執行,只不過執行緒B成功將記錄插到了桶裡面,假設執行緒A插入的記錄計算出來的桶索引和執行緒B要插入的記錄計算出來的桶索引是一樣的,那麼當執行緒B成功插入之後,執行緒A再次被排程執行時,它依然持有過期的連結串列頭但是它對此一無所知,以至於它認為它應該這樣做,如此一來就覆蓋了執行緒B插入的記錄,這樣執行緒B插入的記錄就憑空消失了,造成了資料不一致的行為。另一個不安全的體現是是get操作可能由於resize而死迴圈。

參考:https://www.cnblogs.com/qiumingcheng/p/5259892.html
在這裡插入圖片描述

HashMap和Hashtable的區別

相同點
1 都實現了Map介面
2 Hashtable和HashMap採用的hash/rehash演算法都大概一樣,所以效能不會有很大的差異

不同點
1 hashMap允許NULL作為key和value,而hashtable不允許
2 hashMap執行緒不安全,Hashtable執行緒安全
3 hashMap速度快於hashtable
4 HashMap 把 Hashtable的contains方法去掉了,改成containsvalue和containsKey,避免引起誤會
5 Hashtable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map介面的一個實現

為什麼collection沒有實現clonable介面

Collection介面有很多不同的集合實現形式,而clonable只對具體的物件有意義。

為什map沒有實現collection介面

Set 和List 都繼承了Conllection,Map沒有繼承於Collection介面,Map提供的是key-Value的對映,而Collection代表一組物件。

Map介面的實現有哪些,區別是什麼

HashMap,LinkedHashMap,Hashtable,TreeMap。

LinkedHashMap 是HashMap的一個子類,儲存了記錄的插入順序
Hashtable和HashMap類似,它繼承自Dictionary類,不同的是它不允許鍵或值為空。
TreeMap實現SortMap介面,能夠把它儲存的記錄根據鍵排序,預設是按鍵值的升序排序,也可以指定排序的比較器

執行緒池

Executors框架的四種執行緒池及拒絕策略

四種執行緒池

ExecutorService executorService =
固定大小執行緒池

Executors.newFixedThreadPool(60);
設定固定值會造成高併發執行緒排隊等待空閒執行緒,尤其是當讀取大資料量時執行緒處理時間長而不釋放執行緒,導致無法建立新執行緒。

可快取執行緒池
Executors.newCachedThreadPool();
執行緒池無限大,而系統資源(記憶體等)有限,會導致機器記憶體溢位OOM。

定長且可定時、週期執行緒池
Executors.newScheduledThreadPool(5);

單執行緒執行緒池
Executors.newSingledThreadPool();

/* 自定義執行緒池。
		 * 構造引數:
		 * public ThreadPoolExecutor(
			 * int corePoolSize,--當前執行緒池核心執行緒數
			 * int maximumPoolSize,--當前執行緒池最大執行緒數
			 * long keepAliveTime,--保持活著的空間時間
			 * TimeUnit unit,--時間單位
			 * BlockingQueue<Runnable> workQueue,--排隊等待的自定義佇列
			 * ThreadFactoty threadFactory,
			 * RejectedExecutionHandler handler--佇列滿以後,其他任務被拒絕執行的方法
		 * ){.........}

在使用有界佇列時,若有新的任務需要執行,

  • 若執行緒池實際執行緒數小於corePoolSize,則優先建立執行緒,
  • 若大於corePoolSize,則會將任務加入佇列,
  • 若佇列已滿,則在匯流排程數不大於maximumPoolSize的前提下,建立新的執行緒,
  • 若執行緒數大於maximumPoolSize,則執行拒絕策略。或其他自定義方式。

JDK拒絕策略

  • AbortPolicy:預設,直接丟擲異常,系統正常工作。
  • DiscardOldestPolicy:丟棄最老的一個請求,嘗試再次提交當前任務。
  • CallerRunsPolicy:只要執行緒池未關閉,該策略直接在呼叫者執行緒中,執行當前被丟棄的任務。用執行緒池中的執行緒執行,而是交給呼叫方來執行, 如果新增到執行緒池失敗,那麼主執行緒會自己去執行該任務,不會等待執行緒池中的執行緒去執行
new ThreadPoolExecutor(   
   2, 3, 30, TimeUnit.SECONDS,    
    new SynchronousQueue<Runnable>(),    
    new RecorderThreadFactory("CookieRecorderPool"),    
     new ThreadPoolExecutor.CallerRunsPolicy());  
  • DiscardPolicy:丟棄無法處理的任務,不給予任何處理。
  • 自定義拒絕策略
    如果需要自定義策略,可以實現RejectedExecutionHandler介面。

Reactor模式

參考:https://www.cnblogs.com/duanxz/p/3696849.html

Reactor單執行緒模型

在這裡插入圖片描述
在這裡插入圖片描述
一個Acceptor執行緒,監聽Accept事件,負責接收客戶端的連線SocketChannel,SocketChannel註冊到Selector上並關心可讀可寫事件。
一個Reactor執行緒,負責輪訓selector,將selector註冊的就緒事件的key讀取出來,拿出attach任務Handler根據事件型別分別去執行讀寫等。

單執行緒模型的瓶頸
比如:拿一個客戶端來說,進行多次請求,如果Handler中資料讀出來後處理的速度比較慢(非IO操作:解碼-計算-編碼-返回)會造成客戶端的請求被積壓,導致響應變慢!
所以引入Reactor多執行緒模型!

Reactor多執行緒模型

在這裡插入圖片描述
在這裡插入圖片描述

Reactor多執行緒就是把Handler中的IO操作,非IO操作分開。
操作IO的執行緒稱為IO執行緒,操作非IO的執行緒叫做工作執行緒
客戶端的請求(IO操作:讀取出來的資料)可以直接放進工作執行緒池(非IO操作:解碼-計算-編碼-返回)中,這樣非同步處理,客戶端傳送的請求就得到返回了不會一直阻塞在Handler中。
但是當使用者進一步增加的時候,Reactor執行緒又會出現瓶頸,因為Reactor中既有IO操作,又要響應連線請求。為了分擔Reactor的負擔,所以引入了主從Reactor模型!

主從Reactor模型

在這裡插入圖片描述
在這裡插入圖片描述
  主Reactor用於響應連線請求,從Reactor用於處理IO操作請求!
  特點是:服務端用於接收客戶端連線的不再是1個單獨的NIO執行緒(Acceptor執行緒),而是一個獨立的NIO執行緒池。
  Acceptor執行緒池接收到客戶端TCP連線請求處理完成後(可能包含接入認證等),將新建立的SocketChannel註冊到I/O執行緒池(sub reactor執行緒池)的某個I/O執行緒上,由它負責SocketChannel的讀寫和編解碼工作。
  Acceptor執行緒池只用於客戶端的登入、握手和安全認證,一旦鏈路建立成功,就將鏈路註冊到後端subReactor執行緒池的I/O執行緒上,有I/O執行緒負責後續的I/O操作。
  第三種模型比起第二種模型,是將Reactor分成兩部分,mainReactor負責監聽server socket,accept新連線,並將建立的socket分派給subReactor。subReactor負責多路分離已連線的socket,讀寫網 絡資料,對業務處理功能,其扔給worker執行緒池完成。通常,subReactor個數上可與CPU個數等同。

JVM

Object的記憶體佈局

在這裡插入圖片描述

方法區解除安裝Class的條件

1 該類所有的例項已經被回收
2 載入該類的ClassLoader已經被回收
4該類對應的java.lang.Class物件沒有任何地方被引用

Ps:方法區除了回收無用class,也回收廢棄常量,即沒有被引用常量

可以作為GC Roots的物件包括哪些

虛擬機器棧(棧幀中的區域性變數表)中引用的變數
方法區中類靜態屬性引用的物件
方法區中常量引用的物件
本地方法棧中JNI引用的變數

JVM執行時記憶體模型

方法區、堆、虛擬機器棧、本地方法棧、程式計數器

Netty的ByteBuffer的引用計數器機制

從netty的4.x版本開始,netty使用引用計數機制進行部分物件的管理,通過該機制netty可以很好的實現自己的共享資源池。
如果應用需要一個資源,可以從netty自己的共享資源池中獲取,新獲取的資源物件的引用計數被初始化為1,可以通過資源物件的retain方法增加引用計數,當引用計數為0的時候該資源物件擁有的資源將會被回收。

判斷物件是否存活的兩種方法

1 引用計數法:缺點是對迴圈引用的物件無法回收
2 可達性分析

Java物件的初始化過程

在這裡插入圖片描述

類載入雙親委派模型

從上到下分三個類載入器:

BootStrap classloader:啟動類載入器,負責將Java_HOME/lib下的類庫載入到虛擬機器記憶體中,比如rt.jar
Extension classloader:擴充套件類載入器,負責將JAVA_HOME/lib/ext下的類庫載入到虛擬機器記憶體中。
Application classloader:應用程式類載入器,負責載入classpath環境變數下指定的類庫。如果程式中沒有自定義過類載入器,那麼這個就是程式中預設的類載入器。

雙親委派模型:

  如果一個類載入器收到類載入的請求,它首先不會自己去嘗試載入這個類,而是把這個請求委派給父類載入器完成。每個類載入器都是如此,只有當父載入器在自己的搜尋範圍內找不到指定的類時(即ClassNotFoundException),子載入器才會嘗試自己去載入。
在這裡插入圖片描述
防止自定義的一些跟jdk標準庫中衝突的全限定名的類被載入,導致標準庫函式不可用。

Zookeeper

Zookeeper的常用應用場景有哪些

  • 分散式鎖:獲取父節點下的最小節點作為獲得鎖的一方
  • 命名服務:通過在zookeeper節點下建立全域性唯一的一個path
  • 配置管理:配置放在zk上,所有應用監聽節點改變。
  • 叢集管理:GroupMembers叢集管理,是否有機器退出和加入

Zookeeper的分散式資料一致性演算法

ZAB原子訊息廣播協議

一種是基於basic paxos實現的,另外一種是基於fast paxos演算法實現的。

參考:
http://www.360doc.com/content/16/0823/11/14513665_585293946.shtml
ZAB協議定義了選舉(election)、發現(discovery)、同步(sync)、廣播(Broadcast)四個階段;

Zk啟動過程的Leader選舉分析及資料同步

參考:http://www.cnblogs.com/leesf456/p/6107600.html

Zookeeper資料同步的簡單描述

  在ZooKeeper中所有的客戶端事務請求都由一個主伺服器也就是Leader來處理,其他伺服器為Follower,Leader將客戶端的事務請求轉換為事務Proposal,並且將Proposal分發給叢集中其他所有的Follower,然後Leader等待Follwer反饋,當有過半數(>=N/2+1)的Follower反饋資訊後,Leader將再次向叢集內Follower廣播Commit資訊,Commit為將之前的Proposal提交;

ZK叢集最少需要幾臺機器?

三臺,2N+1,保證奇數,主要是為了leader選舉演算法中的“超過半數有效(>=N/2+1)”

Zookeeper和Eureka的區別

:ZK保證Cp,即一致性,分割槽容錯性,比如當master節點因為網路故障和其他節點失去聯絡的時候,剩餘節點會重新進行Master選舉。問題在於Master選舉的時間太長30~210s,選舉期間整個zk叢集是不可用的,這就導致選舉期間的註冊服務癱瘓。
  Eureka保證Ap,高可用性,它沒有所謂主從節點概念,各節點平等。某節點掛掉不影響其他節點功能,其他節點照樣提供查詢和註冊功能。Eureka客戶端發現Eureka節點掛掉直接切換到其他正常的節點上去。只不過可能查到的資料不是最新的,也就是Eureka不保證資料的強一致性。
  作為註冊中心,推薦Eureka,因為註冊服務更重要的是可用性。

Mysql

InnoDB和MyISAM儲存引擎的區別

Starting from MySQL 5.5.5, the default storage engine for new tables isInnoDB rather than MyISAM.
在這裡插入圖片描述

Btree索引和Hash索引的區別

Btree索引適合範圍查詢,Hash索引適合精確查詢

資料庫的ACID特性

資料庫事務必須具備ACID特性
原子性:Atomic,所有的操作執行成功,才算整個事務成功
一致性:Consistency,不管事務success或fail,不能破壞關係資料的完整性以及業務邏輯上的一致性
隔離性:Isolation,每個事務擁有獨立資料空間,多個事務的資料修改相互隔離。事務檢視資料更新時,資料要麼是另一個事務修改前的狀態,要麼是修改後狀態,不應該檢視到中間狀態資料。
永續性:Durability,事務執行成功,資料必須永久儲存。重啟DB,資料需要恢復到事務執行成功後的狀態。
原子性、一致性、永續性DBMS通過日誌來實現。
  隔離性DBMS通過鎖來實現

Mysql資料庫的隔離級別

M
在這裡插入圖片描述

Select For Update使用場景

  select for update 的使用場景,為了避免自己看到的資料並不是資料庫儲存的最新資料並且看到的資料只能由自己修改,需要用 for update 來限制。

分散式事務模型之XA和TCC的區別和聯絡?

XA-DTP模型

  最早的分散式事務模型是 X/Open 國際聯盟提出的 X/Open Distributed Transaction Processing(DTP)模型,也就是大家常說的 X/Open XA 協議,簡稱XA 協議。
  DTP 模型中包含一個全域性事務管理器(TM,Transaction Manager)和多個資源管理器(RM,Resource Manager)。全域性事務管理器負責管理全域性事務狀態與參與的資源,協同資源一起提交或回滾;資源管理器則負責具體的資源操作。

TCC模型

TCC(Try-Confirm-Cancel)分散式事務模型相對於 XA 等傳統模型,其特徵在於它不依賴資源管理器(RM)對分散式事務的支援,而是通過對業務邏輯的分解來實現分散式事務。
Try-Confirm-Cancel
Try 操作對應2PC 的一階段準備(Prepare);Confirm 對應 2PC 的二階段提交(Commit),Cancel 對應 2PC 的二階段回滾(Rollback),可以說 TCC 就是應用層的 2PC。

參考
https://mp.weixin.qq.com/s?__biz=MzUzMzU5Mjc1Nw==&mid=2247483681&idx=1&sn=05845495c5ef33683addd98fffc72106&chksm=faa0eefbcdd767edbf46cea6f223b426e276dd4d9b19cce64f59387590818f5e4eb96c7d2533&mpshare=1&scene=2&srcid=0118GSYShGZaOyCndUoAqsae&from=timeline#rd

Mysql-binlog日誌複製方式

①基於段的複製
記錄的是執行的語句
②基於行的複製
記錄是表中每一行的操作
③混合複製

mysql主從複製原理

在這裡插入圖片描述
從伺服器的IO執行緒讀取主伺服器的二進位制日誌變更,寫入到中繼日誌relaylog中,如果IO執行緒追趕上了主伺服器的日誌,則進入sleep狀態,直到主伺服器傳送喚醒訊號,從伺服器上的SQL執行緒重放relaylog中的日誌。

基於日誌點的複製和GTID的複製有何區別?

基於日誌點的複製:從主伺服器的哪個二進位制日誌的偏移量進行增量同步,如果指定錯誤會造成遺漏或重複。
基於GTID的複製:從伺服器會告訴主伺服器,已經在從伺服器上已經執行完了哪些gtid值,然後主庫會把從庫未執行的事務gtid值傳送給從庫執行。同一個事務只在指定的從庫上執行一次。

Mysql效能診斷和優化

聚簇索引和非聚簇索引的區別

聚簇索引,就是指主索引檔案和資料檔案為同一份檔案,聚簇索引主要用在Innodb儲存引擎中。如主鍵。B+Tree的葉子節點上的data就是資料本身。
非聚簇索引就是指B+Tree的葉子節點上的data,並不是資料本身,而是資料存放的地址

訊息佇列

消費者當機:怎麼保證訊息佇列訊息不丟失?

  比如activemq或者rabbitmq生產者訊息投遞到訊息佇列後,消費者拿到訊息後,預設是自動簽收機制,訊息佇列將刪除這條訊息,但是如果僅僅是拿到但是沒有來得及處理業務邏輯時,消費者就當機,那麼此訊息將會丟失,以後也不會再收到。
解決辦法
  消費端要設定簽收機制為手動簽收,只有當訊息最終被處理,才告訴訊息佇列已經消費,此時訊息佇列再刪除這條訊息。

MQ叢集當機:怎麼保證訊息不丟失?

  生產者投遞訊息到mq伺服器,如果不保證訊息和佇列的持久化,那麼當mq當機時訊息將徹底丟失,所以需要對訊息做持久化儲存,可以儲存到磁碟或者資料庫中,當mq伺服器恢復時,消費端可以繼續消費mq伺服器中的訊息。

  但是,比如RabbitMQ的訊息持久化,是不承諾100%的訊息不丟失的!
 &emsp**;原因**:因為有可能RabbitMQ接收到了訊息,但是還沒來得及持久化到磁碟,他自己就當機了,這個時候訊息還是會丟失的。如果要完全100%保證寫入RabbitMQ的資料必須落地磁碟,不會丟失,需要依靠其他的機制。

參考:
https://mp.weixin.qq.com/s/ZAWPRToPQFcwzHBf47jZ-A
https://mp.weixin.qq.com/s/HwAc6o8jdIHQTnE3ghXhIA
https://mp.weixin.qq.com/s/AEn3j2lVJOHZx9yegwTsvw
https://mp.weixin.qq.com/s/uqWIf0MAet_StszuOrZCwQ
https://mp.weixin.qq.com/s/9SFrwaCCLnNyuCqP_KQ0zw
https://mp.weixin.qq.com/s/vZ4KVC2eGmssnQUyIKgzfw
https://mp.weixin.qq.com/s/r2_o5wa6Gn94NY4ViRnjpA

Spring原始碼系列

springmvc如何解決迴圈依賴的問題

  當使用構造器方式初始化一個bean,而且此時多個Bean之間有迴圈依賴的情況,spring容器就會丟擲異常!
解決辦法:初始化bean的時候(注意此時的bean必須是單例,否則不能提前暴露一個建立中的bean)使用set方法進行注入屬性,此時bean物件會先執行構造器例項化,接著將例項化後的bean放入一個map中,並提供引用。當需要通過set方式設定bean的屬性的時候,spring容器就會從map中取出被例項化的bean。比如A物件需要set注入B物件,那麼從Map中取出B物件即可。以此類推,不會出現迴圈依賴的異常。

spring事務的傳播行為和隔離級別

spring事務七個事務傳播行為

在TransactionDefinition介面中定義了七個事務傳播行為:

  • PROPAGATION_REQUIRED 如果存在一個事務,則支援當前事務。如果沒有事務則開啟一個新的事務。
  • PROPAGATION_SUPPORTS 如果存在一個事務,支援當前事務。如果沒有事務,則非事務的執行。但是對於事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少許不同。
  • PROPAGATION_MANDATORY 如果已經存在一個事務,支援當前事務。如果沒有一個活動的事務,則丟擲異常。
  • PROPAGATION_REQUIRES_NEW 總是開啟一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起。
  • PROPAGATION_NOT_SUPPORTED 總是非事務地執行,並掛起任何存在的事務。
  • PROPAGATION_NEVER 總是非事務地執行,如果存在一個活動事務,則丟擲異常
  • PROPAGATION_NESTED如果一個活動的事務存在,則執行在一個巢狀的事務中. 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行

Spring事務的五種隔離級別

在TransactionDefinition介面中定義了五個不同的事務隔離級別

  • ISOLATION_DEFAULT 這是一個PlatfromTransactionManager預設的隔離級別,使用資料庫預設的事務隔離級別.
    另外四個與JDBC的隔離級別相對應
  • ISOLATION_READ_UNCOMMITTED 這是事務最低的隔離級別,它充許別外一個事務可以看到這個事務未提交的資料。這種隔離級別會產生髒讀,不可重複讀和幻像讀
  • ISOLATION_READ_COMMITTED 保證一個事務修改的資料提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的資料。這種事務隔離級別可以避免髒讀出現,但是可能會出現不可重複讀和幻像讀。
  • ISOLATION_REPEATABLE_READ 這種事務隔離級別可以防止髒讀,不可重複讀。但是可能出現幻像讀。它除了保證一個事務不能讀取另一個事務未提交的資料外,還保證了避免下面的情況產生(不可重複讀)。
  • ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。除了防止髒讀,不可重複讀外,還避免了幻像讀。

設計模式

單例模式

1懶漢模式-非安全
懶漢模式(執行緒不安全,可能出現多個Singleton 例項)

public class Singleton { 
    private static Singleton instance; 
    private Singleton (){} 

    public static Singleton getInstance() { 
    		if (instance == null) { 
           instance = new Singleton(); 
    		} 
    		return instance; 
    } 
}

2懶漢模式-安全
懶漢模式 (執行緒安全)

public class Singleton { 
    private static Singleton instance; 
private Singleton (){} 

    public static synchronized Singleton getInstance() { 
    		if (instance == null) { 
        	 instance = new Singleton(); 
    		} 
    		return instance; 
    } 
}  

3餓漢模式

public class Singleton { 
    private static Singleton instance = new Singleton(); 
    private Singleton (){} 
    public static Singleton getInstance() { 
         return instance; 
    } 
}

4餓漢模式(變種)
餓漢(變種,跟第三種差不多,都是在類初始化即例項化instance)

public class Singleton { 
    private Singleton instance = null; 
    static { 
    		instance = new Singleton(); 
    } 
    private Singleton (){} 
    public static Singleton getInstance() { 
    		return this.instance; 
    } 
}

5靜態內部類
靜態內部類,跟三四有細微差別:
Singleton類被裝載instance不一定被初始化,因為內部類SingletonHolder沒有被主動使用,只有顯示呼叫getInstance才會顯示裝載SingletonHolder 類,從而例項化instance

public class Singleton { 
    private static class SingletonHolder { 
         private static final Singleton INSTANCE = new Singleton(); 
    } 
    private Singleton (){} 
    public static final Singleton getInstance() { 
         return SingletonHolder.INSTANCE; 
    } 
}   

6列舉
列舉(既可以避免多執行緒同步問題,還可以防止被反序列化重建物件)

public enum Singleton { 
    INSTANCE;
public void whateverMethod() {
	   
} 

public static void main(String[] args) {
		Singleton s = Singleton.INSTANCE;
		Singleton s2 = Singleton.INSTANCE;
		System.out.println(s==s2);
}

}

**輸出結果:**true
  說明這種方式建立的物件是同一個,因為列舉類中的INSTANCE是static final型別的,只能被例項化一次。對於Enum中每一個列舉例項,都是相當於一個單獨的Singleton例項。所以借用 《Effective Java》一書中的話,
單元素的列舉型別已經成為實現Singleton的最佳方法

7懶漢升級版

public class Singleton { 
    private volatile static Singleton singleton; 
    private Singleton (){} 
    public static Singleton getSingleton() { 
    		if (singleton == null) { 
        		synchronized (Singleton.class) { 
        			if (singleton == null) { 
          			  	singleton = new Singleton(); 
       			 } 
        		} 
    		} 
    		return singleton; 
    	} 
}

策略模式

在這裡插入圖片描述

JDK原始碼

ThreadLocal的實現原理

ThreadLocal的實現原理,有什麼缺點?跟執行緒池結合使用要注意什麼
在這裡插入圖片描述
原理:Current Thread當前執行緒中有一個ThreadLocalMap物件,它的key是ThreadLocal的弱引用,Value是ThreadLocal呼叫set方法設定的物件值。每一個執行緒維護一個各自的ThreadLocalMap,所以多個執行緒之間變數相互隔離,互不干擾。

缺點:存在記憶體洩漏問題,因為當ThreadLocal設定為null後,ThreadLocalMap的key的弱引用指向了null,又沒有任何的強引用指向threadlocal,所以threadlocal會被GC回收掉。但是,ThreadLocalMap的Value不會被回收,CurrentThread當前執行緒的強引用指向了ThreadLocalMap,進而指向了這個Entry<key,value>,所以只有當currentThread結束強引用斷開後,currentThread、ThreadLocalMap、Entry將全部被GC回收。
所以結論是
  只要currentThread被GC回收,就不會出現記憶體洩漏。
但是在currentThread被GC回收之前,threadlocal設定為null之後的這段時間裡,Value不會被回收,比如當使用執行緒池的時候,執行緒結束不會被GC回收,會被繼續複用,那這個Value肯定還會繼續存在。如果這個Value很大的情況下,可能就會記憶體洩漏。
雖然threadlocal的set和get方法執行時會清除key為null的value,但是如果當前執行緒在使用中沒有呼叫threadlocal的set或者get方法一樣可能會記憶體洩漏。

跟執行緒池結合使用的注意事項
  因為執行緒池中執行緒複用的情況,本次的threadlocal中可能已經存在資料,所以上一次使用完threadlocal的變數後,要呼叫threadlocal的remove方法清除value。而且要注意呼叫完remove後應該保證不會再呼叫get方法。

AQS實現公平鎖和非公平鎖

基於AQS的鎖(比如ReentrantLock)原理大體是這樣:

  •   有一個state變數,初始值為0,假設當前執行緒為A,每當A獲取一次鎖,status++. 釋放一次,status–.鎖會記錄當前持有的執行緒。
  •   當A執行緒擁有鎖的時候,status>0. B執行緒嘗試獲取鎖的時候會對這個status有一個CAS(0,1)的操作,嘗試幾次失敗後就掛起執行緒,進入一個等待佇列。
  •   如果A執行緒恰好釋放,–status==0, A執行緒會去喚醒等待佇列中第一個執行緒,即剛剛進入等待佇列的B執行緒,B執行緒被喚醒之後回去檢查這個status的值,嘗試CAS(0,1),而如果這時恰好C執行緒也嘗試去爭搶這把鎖。

非公平鎖實現
C直接嘗試對這個status CAS(0,1)操作,併成功改變了status的值,B執行緒獲取鎖失敗,再次掛起,這就是非公平鎖,B在C之前嘗試獲取鎖,而最終是C搶到了鎖。
公平鎖
C發現有執行緒在等待佇列,直接將自己進入等待佇列並掛起,B獲取鎖

RPC

RPC的序列化方式有哪些

Thrift—facebook
ProtoBuf—google
Hessian
JAVA原生的序列化介面
Json/xml

服務熔斷與服務降級概念

服務熔斷:

  一般指某個服務的下游服務出現問題時採用的手段,而服務降級一般是從整體層面考慮的。下游服務出現問題時可以進行服務熔斷。
  對於目標服務的請求和呼叫大量超時或失敗,這時應該熔斷該服務的所有呼叫,並且對於後續呼叫應直接返回,從而快速釋放資源,確保在目標服務不可用的這段時間內,所有對它的呼叫都是立即返回,不會阻塞的。再等到目標服務好轉後進行介面恢復。

服務降級:

  當伺服器壓力劇增的情況下,根據當前業務情況及流量對一些服務和頁面有策略的降級,以此釋放伺服器資源以保證核心任務的正常執行。

其他整理

ThreadLocalMap的線性探測法、HashMap的拉鍊法。兩種解決hash碰撞的方式有何不同?


Netty的RPC如何實現


Netty中原始碼inbound和outbound有啥區別?


怎麼分庫分表可以做到多維度查詢


Fork/Join框架


JAVA執行緒執行中怎麼kill掉

1 通過設定全域性變數標誌來控制執行緒的任務執行完成.進而銷燬執行緒
2 如果執行緒處於長久的阻塞狀態,可以interrupt脫離執行緒阻塞狀態跳出程式體

HA主備怎麼預防腦裂

一般採用2個方法

  1. 仲裁
    當兩個節點出現分歧時,由第3方的仲裁者決定聽誰的。這個仲裁者,可能是一個鎖服務,一個共享盤或者其它什麼東西。
  2. fencing
    當不能確定某個節點的狀態時,通過fencing把對方幹掉,確保共享資源被完全釋放,前提是必須要有可靠的fence裝置。

性別欄位是否需要加索引

1.聚集索引,葉子節點儲存行記錄,InnoDB索引和記錄是儲存在一起的。
2.普通索引,葉子節點儲存了主鍵的值。
在InnoDB引擎中每個表都會有一個聚集索引,如果表定義了主鍵,那主鍵就是聚集索引.一個表只有一個聚集索引,其餘為普通索引.如果性別sex欄位定義為普通的索引,那麼在使用普通索引查詢時,會先載入普通索引,通過普通索引查詢到實際行的主鍵,用主鍵通過聚集索引去查詢最終的行.
如果不對sex性別欄位加索引,那麼查詢過程就是直接全表掃描查詢到聚集索引定位到行,而不需要普通索引和聚集索引的切換,所以效率可能更高一點.
在這裡插入圖片描述

Https的SSL握手過程

Https協議由兩部分組成:http+ssl,即在http協議的基礎上增加了一層ssl的握手過程.

  • 瀏覽器作為發起方,向網站傳送一套瀏覽器自身支援的加密規則,比如客戶端支援的加密演算法,Hash演算法,ssl版本,以及一個28位元組的隨機數client_random
  • .網站選出一套加密演算法和hash演算法,生成一個服務端隨機數server_random並以證照的形式返回給客戶端瀏覽器,這個證照還包含網站地址、公鑰public_key、證照的頒發機構CA以及證照過期時間。
  • .瀏覽器解析證照是否有效,如果無效則瀏覽器彈出提示框告警。如果證照有效,則根據server_random生成一個preMaster_secret和Master_secret[會話金鑰], master_secret 的生成需要 preMaster_key ,並需要 client_random 和 server_random 作為種子。瀏覽器向伺服器傳送經過public_key加密的preMaster_secret,以及對握手訊息取hash值並使用master_secret進行加密傳送給網站.[客戶端握手結束通知,表示客戶端的握手階段已經結束。這一項同時也是前面傳送的所有內容的hash值,用來供伺服器校驗]
  • .伺服器使用private_key 解密後得到preMaster_secret,再根據client_random 和 server_random 作為種子得到master_secret.然後使用master_secret解密握手訊息並計算hash值,跟瀏覽器傳送的hash值對比是否一致.
    然後把握手訊息通過master_secret進行對稱加密後返回給瀏覽器.以及把握手訊息進行hash且master_secret加密後發給瀏覽器.[伺服器握手結束通知,表示伺服器的握手階段已經結束。這一項同時也是前面傳送的所有內容的hash值,用來供客戶端校驗。]
  • .客戶端同樣可以使用master_secret進行解密得到握手訊息.校驗握手訊息的hash值是否跟伺服器傳送過來的hash值一致,一致則握手結束.通訊開始
  • .以後的通訊都是通過master_secret+對稱加密演算法的方式進行. 客戶端與伺服器進入加密通訊,就完全是使用普通的HTTP協議,只不過用"會話金鑰"加密內容。SSL握手過程中如果有任何錯誤,都會使加密連線斷開,從而阻止了隱私資訊的傳輸
    在這裡插入圖片描述
    非對稱加密演算法:RSA,DSA/DSS
    對稱加密演算法:AES,RC4,3DES
    HASH演算法:MD5,SHA1,SHA256
    參考: http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html

select和epoll的區別

1 select有最大併發數限制,預設最大檔案控制程式碼數1024,可修改。
epoll沒有最大檔案控制程式碼數限制,僅受系統中程式能開啟的最大檔案控制程式碼限制。
2 select效率低,每次都要線性掃描其維護的fd_set集合。
epoll只在集合不為空才輪訓
3select存在核心空間和使用者空間的記憶體拷貝問題。
  epoll中減少記憶體拷貝,mmap將使用者空間的一塊地址和核心空間的一塊地址同時對映到相同的一塊實體記憶體地址

NIO使用的多路複用器是epoll

Epoll導致的selector空輪詢

  Java NIO Epoll 會導致 Selector 空輪詢,最終導致 CPU 100%
  官方聲稱在 JDK 1.6 版本的 update18 修復了該問題,但是直到 JDK 1.7 版本該問題仍舊存在,只不過該 BUG 發生概率降低了一些而已,它並沒有得到根本性解決
Netty的解決方案:
  對 Selector 的 select 操作週期進行統計,每完成一次空的 select 操作進行一次計數,若在某個週期內連續發生 N 次空輪詢,則判斷觸發了 Epoll 死迴圈 Bug。
  然後,Netty 重建 Selector 來解決。判斷是否是其他執行緒發起的重建請求,若不是則將原 SocketChannel 從舊的 Selector 上取消註冊,然後重新註冊到新的 Selector 上,最後將原來的 Selector 關閉。

正排索引和倒排索引

正排索引

也叫正向索引,正排表是以document文件的ID為關鍵字,記錄了document文件中所有的關鍵字keyword的資訊,所以在查詢某個keyword的時候,會掃描正排表中每個document文件,直到查出所有包含keyword的文件。
圖示:
在這裡插入圖片描述

倒排索引

也叫反向索引,倒排表示以keyword關鍵字建立索引,關鍵詞keyword所對應的的表項記錄了出現這個keyword的所有文件。表項包含了該文件的ID和在該文件中出現的位置情況。
倒排表一次可以查出keyword關鍵字對應的所有文件,效率高於正排表。
在這裡插入圖片描述

正排索引是從文件到關鍵字的對映(已知文件求關鍵字)
倒排索引是從關鍵字到文件的對映(已知關鍵字求文件)

後記

  本人到目前為止接觸java有七年多的時間,越來越覺得語言的工具化,以工具化的角度去看待自己的技術,上邊這些面試題目雖然在實際中都基本上會被問到,但是不應該投機取巧,還是少一些功利化的東西,功利化作為內驅沒錯,但是學習技術還是得靜下心,沉住氣,保持耐心.真正的實力絕對不是靠這些面試題達成的,絕對是日復一日的堅持和忍耐

個人公眾號-offer驛站
在這裡插入圖片描述

其他優質文章
《成就一億技術人,我在CSDN的這九年》
《計算機如何做減法?10個程式設計師9個不知道!!!》
《高階程式語言學習概論》
《網際網路三高架構之高併發和高效能的理解》
《《跟任何人都能聊得來》讀書筆記》
《這十年裡的迷茫路口》
《一個碼農的那五年》
《搞技術的總要做點高逼格的事情,那些lowB操作留給新人練手吧!》
《教你搭建一套自己的SVN伺服器》
歡迎下方留言跟我一起交流,讓我知道您來過~

相關文章