1.ThreadLocaljava
ThreadLocal實現線程本地存儲的功能,同一個ThreadLocal所包含的對象,在不一樣的Thread中有不一樣的實例,獲取ThreadLocal對象時實際上是在Thread類中的Map類型的threadLocals變量中經過ThreadLocal變量爲鍵值進行獲取。node
2.volatile的做用和原理mysql
被volatile修飾的變量保證Java內存模型中的可見性和有序性。linux
volaitle底層是經過內存屏障來實現可見性和有序性。內存屏障是一個CPU的指令,他的做用有兩個,一是保證特定操做的執行順序,二是保證某些變量的內存可見性。內存屏障告訴編譯器和CPU,無論什麼指令都不能和這條內存屏障指令重排序,另外一個做用是強制刷出各類CPU的緩存資源,所以任何CPU上的線程都能讀取到這些數據的最新版本。redis
3.J.U.C中的鎖算法
Java提供了兩種鎖機制來控制多個線程對共享資源的互斥訪問。sql
實現 | 公平鎖 | 等待可中斷 | 條件 | 性能 | |
synchronized | JVM | 非公平 | 不可中斷 | / | 大體 |
ReentrantLock | JDK | 非公平/公平 | 可中斷 | 可綁定多個Condition | 相同 |
除非要使用ReentrantLock的高級功能,不然優先使用synchronized,synchronized是JVM實現的一種鎖機制,JVM原生的支持它,而ReentrantLock不是全部的JDK版本都支持。synchronized鎖釋放由JVM保證,ReentrantLock須要顯式的釋放。數據庫
4.atomic包裏的一些問題數組
atomic是使用volatile和CAS來實現的瀏覽器
5.HashMap的擴容
當HashMap的容量到達threshold時,須要進行動態擴容,將容量擴大爲原來的兩倍,而後將存儲的數據進行rehash。
6.Semaphore信號量用來作什麼?
Semaphore信號量相似於操做系統的信號量,能夠控制對互斥資源的訪問線程數。
7.Java內存模型
CPU和內存之間增長高速緩存。
全部的變量都存儲在主內存中,每一個線程有本身的工做內存,工做內存存儲在高速緩存中,保存了該線程使用變量的拷貝。
線程只能直接操做工做內存中的變量,不一樣線程之間的變量值傳遞須要經過主內存來完成。
內存模型的三大特性:
8.Java內存空間是怎麼分配的?
8.Full GC觸發條件
8.類加載機制
8.新生代和老年代能夠轉換嗎?
對象優先分配在新生代的Eden區,經過長期存活(達到必定歲數)的對象進入老年代和動態對象年齡斷定使對象重新生代進入老年代。
9.這些內存裏面的垃圾怎麼回收?
引用計數法和可達性分析法。回收算法包括:標記-清除、標記-整理、複製、分代收集算法。
10.怎麼判斷是垃圾?GCRoot能夠爲哪些?
可達性分析法中,從GC Root出發,不可達的是能夠被回收的對象。
11.G1收集器
垃圾收集器都存在 Stop The World 的問題,G1對這個問題進行了優化,G1對整個新生代和老年代一塊兒回收,把堆劃分爲多個大小相等的獨立區域region,使得每一個region能夠單獨進行垃圾回收,經過記錄每一個region垃圾回收時間以及回收所得到的空間(經過過去回收的經驗得到),並維護一個優先列表,每次根據容許的收集時間,優先回收價值最大的region。
初始標記 -> 併發標記 -> 最終標記 -> 篩選回收
特色:
8.BIO、NIO、AIO
BIO,同步阻塞IO,一個線程處理一個鏈接,發起和處理IO請求都是同步的
NIO,同步非阻塞IO,一個線程處理多個連接,發起IO請求是非阻塞的,處理IO請求是同步的(輪詢)
AIO,異步非阻塞IO,一個有效請求一個線程,發起和處理IO請求都是異步的。
9.AQS
描述 | |
FutureTask | 用來封裝Callable的返回值 |
BlockingQueue | 當隊列中沒有元素時take()被阻塞,當隊列滿時put()被阻塞 |
ForkJoin | 大的計算任務拆分紅小任務,並行計算 |
10.JUC
描述 CountDownLatch countDown()會使計數器減1,當計數器爲0時,調用await()的線程會被喚醒 CyclicBarrier await()會使計數器減1,當計數器爲0時,全部調用await()的方法會被喚醒 Semaphore 相似於操做系統的信號量,能夠控制對互斥資源的訪問線程數
11.實現線程安全的方法
12.IO與NIO
I/O | NIO | |
數據打包和傳輸方式 | 流 | 塊 |
是否阻塞 | 阻塞 | 非阻塞 |
13.NIO
14.Class.forName()怎麼執行的?
15.守護線程是什麼?守護線程是怎麼退出的?
守護線程是在程序運行時提供後臺服務的線程,不屬於程序運行中不可或缺的部分。
當程序中全部非守護線程結束時,程序也就終止,同時殺死全部的守護線程。
16.Stack與ArrayList的區別
Stack是用Vector實現的,Queue是用ArrayList實現的,因此比較Stack與ArrayList的區別就是比較這二者之間的區別。
17.HashMap的rehash過程
HashMap中使用一個技巧,和將哈希值與舊容量進行&運算,若是位上爲0則在原位置,若是爲1則在下邊。
18.hashcode和equals的區別
equals用來判斷實體在邏輯上是否相等,當重寫equals方法時要重寫hashcode方法。
19.equals和==的區別?我要比較內容呢?
20.Java代碼編譯過程
詞法分析 -> 語法分析 -> 語義分析 -> 字節碼生成
21.如何設計hash函數
22.經常使用的線程池
23.分段鎖
1.運行時數據區域
程序計數器 | JVM棧 | 本地方法棧 | 堆 | 方法區 | 運行時常量池 | |
功能 | 記錄正在執行的虛擬機字節碼指令的地址 | 棧幀用於存儲局部變量表、操做數棧、常量池引用等信息 | 與JVM棧相似,爲本地方法服務 | 對象分配區域,垃圾收集的主要區域 | 用於存訪加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼 | 方法區的一部分,存放生成的字面量和符號引用 |
線程私有 | 線程私有 | 線程私有 | 公有 | 公有 | 公有 | |
垃圾收集 | 不須要 | 不須要 | 不須要 | 須要(垃圾回收的主要區域) | 類的卸載:1.類實例被回收2.加載類的classloader被回收3.class對象沒有被引用 方法區在jdk1.8之前放在永久代中,jdk1.8之後放在本地內存中,而不是JVM內存 |
須要 |
2.垃圾收集算法
新生代 | 老年代 | |||
垃圾收集算法 | 複製(Eden:Survivor) | 標記-清除/標記-整理 | ||
GC | Minor GC | Full GC | ||
觸發條件 | Eden空間滿時 | 1.調用System.gc() 2.老年代空間不足 3.空間分配擔保失敗 |
||
3.類加載過程:
4.引用類型
描述 | |
強引用 | 不會被回收 |
軟引用 | 只有在內存不夠的狀況下才會被回收 |
弱引用 | 必定會被回收,只能存活到下一次垃圾回收發生以前 |
虛引用 | 不會對其生存時間形成影響,惟一目的是在這個對象被回收時收到一個系統通知 |
5.垃圾收集算法
描述 | 不足 | |
標記-清除 | 標記要收集的對象,而後清除 | 標記和清除效率都不高,形成內存碎片 |
標記-整理 | 讓全部存活的對象都向一端移動,而後直接清理掉端邊界之外的內存 | 對標記-清除算法的補充 |
複製 | 將內存劃分爲相等的兩塊,每次只使用其中的一塊,當這一塊內存用完了就將還存活的對象複製到另外一塊上面,而後把使用過的內存空間進行一次清理 | 只使用了內存的一半 |
分代收集 | 他根據對象存活週期將內存劃分爲幾塊,不一樣塊採用適當的收集算法。 通常將堆分爲新生代和老年代。
|
6.垃圾收集器
多線程與單線程 | 串行與並行 | 描述 | 適用場景 | |
Serial收集器 | 單線程 | 串行 | 簡單高效 | Client |
ParNew收集器 | 多線程 | 串行 | Serial的多線程版本 | Server |
Parallel Scavenge收集器 | 多線程 | 串行 | 動態調整以提供最合適的停頓時間或者最大吞吐量 | 注重吞吐量以及CPU資源敏感場合 |
Serial Old收集器 | 單線程 | 串行 | Serial的老年代版本 | Client |
Parallel Old收集器 | 多線程 | 串行 | Parallel Scavenge的老年代版本 | 注重吞吐量以及CPU資源敏感場合 |
CMS收集器 | 多線程 | 串行/並行 | 吞吐量低 沒法處理浮動垃圾 標記-清除致使碎片 |
|
G1收集器 | 多線程 | 串行/並行 | 空間整合:基於 標記-整理 可預測的停頓 |
7.內存分配與回收
描述 | 特色 | 觸發條件 | ||
Minor GC | 回收新生代 | 頻繁,快 | Eden區空間不夠時 | |
Full GC | 回收老年代和新生代 | 少,慢 | 1. System.gc() 2. 老年代不足(大對象、長期存活的對象進入老年代) 3. 空間分配擔保失敗 |
內存分配策略
1.簡述TCP的三次握手、四次揮手,爲何要三次握手?爲何client會進入TIME_WAIT?
TCP的三次握手:
三次握手過程當中主要對序號(seq)、確認序號(ack)、標誌位(ACK、SYN)進行操做。
(1)client端發送鏈接請求:SYN=1(創建新鏈接),seq=x
(2)server端接收請求並返回確認報文:SYN=1(創建新鏈接),ACK=1(ack有效),ack=x+1,seq=y
(3)client接收到確認報文,再次發送確認消息:ACK=1(ack有效),seq=x+1(client上一條請求seq+1),ack=y+1
(4)server端收到確認後,鏈接創建
TCP的四次揮手:
(1)client端發送鏈接釋放報文:FIN=1,seq=u
(2)server收到以後發出確認,此時TCP屬於半關閉狀態,server能向client發送數據反之不能:ACK=1,seq=v ack=u+1
(3)當server處理完畢後,發送鏈接釋放報文:FIN=1,ACK=1,seq=w,ack=u+1
(4)client收到後發出確認,進入TIME-WAIT狀態,等來2MSL(最大報文存活時間)後釋放鏈接:ACK=1,seq=u+1,ack=w+1
(5)server收到client的確認後釋放鏈接
爲何要進行三次握手?
第三次握手時爲了防止失效的鏈接請求到達服務器,讓服務器錯誤打開鏈接。
客戶端發送的鏈接請求若是在網絡中滯留,那麼就會隔很長時間才能收到服務器的鏈接確認。客戶端等待一個超時重傳時間後,就會從新發起請求。可是這個滯留的鏈接請求最後仍是會到達服務器,若是不進行第三次握手,那麼服務器就會打開兩個鏈接。若是有第三次握手,客戶端會忽略服務器發送的對滯留鏈接請求的鏈接確認,不進行第三次握手,所以就不會再次打開鏈接。
爲何會有TIME_WAIT?
客戶端接收到服務器的FIN報文後進入TIME_WAIT狀態而不是CLOSED,還須要等待2MSL,理由:
確保最後一個確認報文可以到達。若是server端沒收到client端發來的確認報文,那麼就會從新發送鏈接釋放請求報文。
爲了讓本鏈接持續時間內所產生的全部報文都從網絡中消失,使得下一個新的鏈接不會出現舊的鏈接請求報文。
2.TCP的擁塞控制
慢開始:最初,發送方只能發送一個報文段(假設),當收到確認後,將擁塞窗口(cwnd)加倍,呈指數型增加
擁塞避免:設置一個慢開始門限ssthresh,當cwnd>=ssthresh,進入擁塞避免,每一個輪次只將cwnd加1
快重傳:在接收方,要求每次接收到報文段都應該對最後一個已收到的有序報文段進行確認。例如已經接收到M1和M2,此時收到M4,應該發送對M2的確認。在發送方,若是收到三個重複確認,那麼能夠知道下一個報文段丟失,此時執行快重傳,當即重傳下一個報文段。
快恢復:在這種狀況下,只是丟失個別報文段,不是網絡擁塞,所以執行快恢復,令ssthresh=cwnd/2,cwnd=ssthresh,此時直接進入擁塞避免。
3.瀏覽器輸入url請求服務器的過程,分析其中哪些部分用到緩存。
輸入url
瀏覽器查找瀏覽器緩存
若瀏覽器緩存中未找到,查找本機host文件
若本機host文件中未找到,則查找路由器、ISP緩存
若路由器、ISP緩存中未找到,則向配置的DNS服務器發起請求查找(若本地域名服務器未找到,會向根域名服務器->頂級域名服務器->主域名服務器)
獲取到url對應的ip後,發起TCP三次握手
發送http請求,將響應顯示在瀏覽器頁面中
四次揮手結束
4.ARP(地址解析協議)
ARP實現由IP地址獲得MAC地址。
主機A知道主機B的IP地址,可是ARP高速緩存中沒有該IP地址到MAC地址的映射,此時主機A經過廣播的方式發送ARP請求分組,主機B收到該請求後會發送ARP響應分組給主機A告知其MAC地址,隨後主機A向其高速緩存中寫入主機B的IP地址到MAC地址的映射。
5.HTTP的流量控制,具體的控制算法
流量控制是爲了控制發送方發送速率,保證接收方來得及接收。
接收方發送的確認報文中的窗口字段能夠用來控制發送方窗口大小,從而影響發送方的發送速率。
6.計算機網絡體系結構
設備 | 地址 | 通訊方式 | 數據單位 | 協議 | 描述 | ||
應用層 | 報文 | HTTP、DNS、FTP、DHCP、SMTP(郵件發送)、POP3和IMAP(郵件接收) | 爲特定應用程序提供數據傳輸服務 | ||||
傳輸層 | 報文段 | TCP
|
爲進程提供數據傳輸服務 | ||||
用戶數據報 |
UDP
|
||||||
網絡層 | 路由器(路由選擇和分組轉發) 路由協議選擇:RIP/OSPF(內部) BGP(外部) |
IP地址 | 分組 | IP協議(分類、子網劃分、無分類) NAT:將本地IP轉換爲全球IP |
爲主機提供數據傳輸服務 | ||
地址解析協議(ARP):由IP地址獲得MAC地址 |
|||||||
網際控制報文協議(ICMP):封裝在IP數據包中,但不屬於高層協議,是爲了更有效地轉發IP數據包和提升交付成功的機會。 |
|||||||
網際組管理協議(IGMP) |
|||||||
數據鏈路層 | 交換機(自學習交換表:MAC地址到接口的映射) | 一臺主機有多少個網絡適配器就有多少個MAC地址 | 廣播信道(星型、環形、直線型) | 幀 | 信道複用技術 | 頻分複用 時分複用 統計時分複用 波分複用 碼分複用 |
爲同一鏈路的主機提供數據傳輸服務 |
點對點信道 | CSMA/CD | ||||||
物理層 | 集線器 | 單工通訊 半雙工通訊 全雙工通訊 |
比特 | 在傳輸媒體上傳輸數據比特流 |
在向下的過程當中,須要添加下層協議所須要的首部或者尾部,而在向上的過程當中不斷拆開首部和尾部。
7.路由選擇協議
RIP |
OSPF | BGP | |
名稱 | 開放最短路徑優先 | 邊界網關協議 | |
使用範圍 | 內部 | 內部 | 外部 |
描述 | 基於距離向量的路由選擇協議 | 洪泛法 | 每一個自治系統必須配置BGP發言人,發言人之間經過TCP鏈接來交換路由信息 |
特色 | 實現簡單開銷小 最大距離爲15,限制了網絡規模 故障傳播時間長 |
更新過程收斂快 | 只能尋找一條比較好的路由,而不是最佳路由 |
8.UDP和TCP比較
UDP | TCP | |
鏈接 | 無鏈接 | 面向鏈接 |
可靠 | 盡最大能力交付 | 可靠交付 |
擁塞控制 | 無 | 有 |
面向 | 面向報文 | 面向字節流 |
通訊 | 一對1、一對多、多對一和多對多 | 一對一,全雙工通訊 |
1.HTTP的過程
相似於瀏覽器輸入url請求服務器的過程?
2.HTTPS怎麼創建請求
HTTPS = HTTP + SSL(Secure Sockets Layer, 安全套接字層)
HTTPS 能夠防竊聽(非對稱密鑰加密)、防假裝、防篡改(加密和認證)
客戶端發送請求到服務器端
服務器端返回證書和公開密鑰,公開密鑰做爲證書的一部分而存在
客戶端驗證證書和公開密鑰的有效性,若是有效,則生成共享密鑰並使用公開密鑰加密發送到服務器端
服務器端使用私有密鑰解密數據,並使用收到的共享密鑰加密數據,發送到客戶端
客戶端使用共享密鑰解密數據
SSL加密創建...
3.GET和POST比較
GET | POST | |
做用 | 獲取資源 | 傳輸實體 |
參數 | 查詢字符串 | request body |
安全(不會改變服務器狀態) | 安全 | 不安全 |
冪等性 | 知足 | 不知足 |
緩存 | 可緩存 | 多數狀況下不能夠 |
1.mysql的索引,最左匹配原則
索引能夠加快對數據的檢索。常見的有B+Tree索引,哈希索引。
最左匹配原則:
當索引是聯合索引,在查詢條件中,mysql是從最左邊開始命中的,若是出現了範圍查詢(>、<、between、like),就不能進一步命中了,後續退化爲線性查找,列的排列順序決定了可命中索引的列數。
2.mysql的主從複製
mysql爲了保持高可用,會採用一主多從的結構,一個master節點,多個slave節點,master節點能夠進行寫操做,而slave節點只能進行讀操做。
binlog線程:將主服務器上的數據更改寫入二進制日誌中
I/O線程:從主服務器上讀取二進制日誌,並寫入從服務器的重放日誌中
SQL線程:讀取重放日誌並重放其中的SQL語句
3.mysql的彙集索引、非彙集索引
彙集索引:以主鍵建立的索引,在葉子結點上存儲的是表中的數據
非彙集索引:以非主鍵建立的索引,葉子結點上存儲的是主鍵和索引列
使用非彙集索引查詢出數據時,拿到葉子上的主鍵再去查到想要查找的數據。(回表)
4.mysql聯合索引,要注意什麼?
聯合索引即索引由多個列(a,b,c,d)組成,要注意索引的命中,最左匹配原則,從左開始命中,遇到範圍查詢就不能進一步匹配。
5.爲何數據庫要使用B+樹來實現索引?
更少的查找次數(B+樹相比紅黑樹更矮胖)
利用磁盤預讀特性(一次IO能徹底載入一個節點)
6.MySQL索引
描述 | 特色 | 使用場景 | |
B+ Tree索引 | 使用B+ Tree做爲底層實現 |
對樹進行搜索,查找速度快 分爲聚簇索引和非聚簇索引 |
查找、排序、分組 |
哈希索引 | 使用哈希做爲底層實現 |
沒法用於排序與分組 只支持精確查找,時間複雜度爲O(1) |
當索引值使用的頻繁時,會在B+ Tree索引之上再建立一個哈希索引 |
全文索引 | 全文索引使用倒排索引實現,記錄着關鍵詞到其所在文檔的映射 | 查找文本中的關鍵詞 | |
空間數據索引 | 從全部維度來索引數據 | 用於地理數據存儲 |
索引優化:
索引的優勢:
7.InnoDB和MyISAM比較
InnoDB | MyISAM | |
默認 | 是 | 否 |
隔離級別 | 四個隔離級別 | |
事務 | 支持 | 不支持 |
鎖 | 行級/表級 | 表級 |
外鍵 | 支持 | 不支持 |
備份 | 在線熱備份 | |
崩潰恢復 | 機率高,恢復慢 | |
特性 | 壓縮表和空間數據索引 | |
使用場景 | 讀寫分離的讀表 |
8.切分
9.MySQL數據庫是怎麼插入的?
10.事務怎麼回滾?裏面有什麼日誌?
11.一百萬條數據記錄,如何分頁顯示最後一條?
設一個列從1開始自增,並設爲索引,以這個列爲條件進行查詢。
12.數據庫事務隔離級別,可重複度和可串行化實現的原理
隔離級別:讀未提交、讀已提交、可重複度、串行化
1.數據庫併發一致性問題
數據庫併發一致性問題是因爲隔離性致使的。
2.封鎖
3.多版本併發控制
基於 | 描述 | |
系統版本號 | 系統 | 沒開始一個事務,系統版本號+1 |
事務版本號 | 事務 | 事務開始時的系統版本號 |
建立版本號 | 行數據 | 數據建立時的系統版本號 |
刪除版本號 | 行數據 | 數據刪除時的系統版本號 |
4.異常和數據庫範式
描述 | |
1NF | 屬性不可分 |
2NF | 每一個非主屬性徹底依賴於鍵碼 |
3NF | 每一個非主屬性不傳遞函數依賴於鍵碼 |
5.鏈接
關鍵字 | 描述 | |
內連接 | INNER JOIN | 等值鏈接 |
自鏈接 | INNER JOIN | 本身鏈接本身 |
天然鏈接 | MATURAL JOIN | 全部同名列的等值鏈接 |
外鏈接 | LEFT OUTER JOIN | 保留左表沒有關聯的行 |
RIGHT OUTER JOIN | 保留右表沒有關聯的行 | |
OUTER JOIN | 保留全部沒有關聯的行 |
數據結構
1.B+樹和B樹的區別
B+樹的數據都在葉子結點上,而B樹的非根非葉節點也是數據節點,因此B+樹的查詢更穩定。
B+樹有兩個指針,一個指向root節點,一個指向葉子節點的最左側,所以其範圍查詢效率更高,而B樹則須要中序遍歷B樹。
同階的狀況下,B+樹節點中的關鍵字要比B樹多一個,而且B+樹的中間節點不保存數據,因此磁盤也可以容納更多結點元素,所以B+樹更加矮胖,查詢效率也更高。
2.紅黑樹
紅黑樹是一個自平衡二叉查找樹。時間複雜度O(log n)
3.紅黑樹和平衡二叉樹的區別
平衡二叉樹和高度相關,保持平衡的代價更高(屢次旋轉),所以適用於插入、刪除較少,查詢較多的場景。
紅黑樹和高度無關,旋轉次數較少,所以適用於插入、刪除較多的場景。
1.Mybatis動態代理
2.Spring IOC是什麼?怎麼實現的?
3.Spring IOC裏面的反射機制怎麼實現的?
1.redis分片,客戶端請求怎麼處理?
Redis的分片是指將數據分散到多個Redis實例中的方法,分片以後,每一個redis擁有一部分原數據集的子集。在數據量很是大時,分片能將數據量分散到若干主機的redis實例上,進而減輕單臺redis實例的壓力。
分片的位置:
2.redis的zset底層實現
跳躍表來實現。
跳躍表相比於紅黑樹的優勢:
3.redis和mysql的區別
4.redis加鎖
redis爲單進程單線程模式,採用隊列模式將併發訪問變爲串行訪問,redis自己沒有鎖的概念,但能夠用redis實現分佈式鎖。
Redis Incr 命令將 key 中儲存的數字值增一。 若是 key 不存在,那麼 key 的值會先被初始化爲 0 ,而後再執行 INCR 操做。
分佈式鎖的核心思想是將設置鎖和超時時間、刪除鎖分別做爲一個原子操做進行。
5.redis的淘汰策略
6.redis沒法被命中怎麼辦?會出現什麼問題?
沒法被命中:沒法直接經過緩存獲得想要的數據
解決方案:
7.Redis和MySQL複製和分片
複製 | 分片 | |
MySQL | 三個線程(binlog線程、I/O線程、SQL線程),目的是實現讀寫分離 | 水平切分、垂直切分 |
Redis | 使用RDB快照進行復制,發送期間使用緩衝區記錄執行的寫命令,在RDB快照發送完畢後,發送緩衝區中的寫命令 | 水平切分 |
8.Redis是什麼?Sorted List是什麼?skiplist是什麼?怎麼實現的?怎麼插入一個值?怎麼進行查詢?和其餘數據結構進行對比?
9.Redis的hash和Java的map的區別
1.四種引用類型
2.可達性分析算法的root
可達性分析算法是從GC root出發,只有經過GC root可達的對象是被引用的對象,不可達的對象屬於能夠回收的對象。
1.進程和線程的區別
主線程是什麼?
2.操做系統的內存管理
3.分頁式的頁表放在哪
進程控制塊(PCB)中。
4.進程的PCB裏還有哪些東西?
5.MMU(內存管理單元)
內存管理單元(MMU)管理着地址空間和物理內存的轉換,根據其內存管理方式的不一樣,其中包括基地址寄存器、界限地址寄存器的值以及段表和頁表。
6.進程通訊
7.共享內存
採用共享內存的進程間通訊須要通訊進程創建共享內存區域。一般一塊共享內存區域駐留在生成共享內存段的進程的地址空間。須要使用信號量用來同步對通向存儲的訪問。
8.Inode
9.應用程序是如何讀取文件的?
1.linux腳本,殺掉包含一個關鍵字的全部進程
ps -ef | grep 關鍵字 | awk '{print $2}' | xargs kill -9
2.自旋鎖和互斥鎖
都屬於linux內核中的內核鎖。
互斥鎖經過對共享資源的鎖定和互斥解決利用資源衝突問題,互斥鎖是選擇睡眠的方式來對共享工做中止訪問的。
自旋鎖不會引發調度者睡眠,而是一直循環。
1.linux I/O模型,說說select和epoll的區別
描述 | 特色 | |
阻塞式I/O | 應用進程被阻塞,知道數據從內核緩衝區複製到應用進程緩衝區才返回 | 阻塞期間,其餘進程繼續執行,CPU利用率高 |
非阻塞式I/O | 輪詢I/O是否完成 | 屢次執行系統調用,CPU利用率低 |
I/O複用 | select poll epoll | 單個線程具備處理多個I/O時間的能力 |
信號驅動I/O | 執行系統調用後當即返回,內核在數據到達時嚮應用進程發送SIGIO信號,應用進程收到後將數據從內核複製到應用進程 | CPU利用率比非阻塞I/O高 |
異步I/O | 系統調用當即返回,不會阻塞,內核完成全部操做後向應用進程發送信號 | 異步I/O通知應用進程I/O完成 信號驅動I/O是通知應用進程能夠開始I/O |
select | poll | epoll | |
timeout精度 | 1ns | 1ms | 1ms |
描述符數據結構 | 數組(所以有最大限制) | 鏈表 | 鏈表 |
複製描述符 | 每次調用都須要複製 | 每次調用都須要複製 | 第一次複製、修改 |
返回結果 | 不返回準備好的描述符 | 不返回準備好的描述符 | 準備好的描述符加入到一個鏈表中管理 |
支持 | 幾乎全部系統 | 較新的系統 | Linux系統 |
適用場景 | 實時性高,全部平臺 | 實時性低,描述符適中 | 描述符多,描述符變化小 |
select和epoll的區別
2.多路複用模型
1.分佈式事務
CAP定理:
在分佈式系統中,分區容忍性必不可少,由於須要老是假設網絡是不可靠的。所以,CAP理論其實是要在可用性和一致性作權衡。
BASE:
BASE是基本可用(Basically Available)、軟狀態(Soft State)和最終一致性(Eventually Consistent)三個短語的縮寫。
BASE理論是對CAP中一致性和可用性權衡的結果,它的核心思想是:即便沒法作到強一致性,但每一個應用均可以根據自身業務特色,採用適當的方式來使系統達到最終一致性。
ACID要求強一致性,一般運用在傳統的數據庫系統上。而BASE要求最終一致性,經過犧牲強一致性來達到可用性,一般運用於大型分佈式系統中。
解決方案:
(1)兩階段提交(2PC)
基於XA協議實現
存在問題:1.同步阻塞 2.單點問題 3.數據不一致 4.太過保守
(2)TCC
針對每一個操做,都要註冊一個與其對應的確認和補償操做
Try/Confirm/Cancel
(3)本地消息表(異步確保)
將業務操做和本地消息表放在一個事務中。業界使用最多。
(4)Sagas事務模型
事件溯源,至關於將分佈式系統中的操做都記錄到數據庫日誌表中,要得到最新的狀態,則須要從新執行一遍日誌表的操做。而且能夠dump某一時刻的數據表,在此基礎上執行在這以後的操做。
1.二叉樹的先序遍歷,層序遍歷的實現
private static class Node{ int value; Node left; Node right; public Node(int value) { this.value = value; } } /** * 先序遍歷非遞歸版 * @param root */ public void preOrder1(Node root){ if(root == null) return; Stack<Node> stack = new Stack<>(); stack.push(root); while(!stack.isEmpty()){ Node node = stack.pop(); System.out.println(node.value); if(node.right != null) stack.push(node.right); if(node.left != null) stack.push(node.left); } } /** * 先序遍歷非遞歸版回溯法 * @param root */ public void preOrderBack(Node root){ if(root == null) return; Stack<Node> stack = new Stack<>(); while(root != null || !stack.isEmpty()){ if(root != null){ System.out.println(root.value); stack.push(root); root = root.left; }else{ Node upNode = stack.pop(); root = upNode.right; } } } public void preOrderTrans(Node root){ if(root == null) return; System.out.println(root.value); preOrderTrans(root.left); preOrderTrans(root.right); } public void bfs(Node root){ Queue<Node> queue = new LinkedList<>(); queue.add(root); while(!queue.isEmpty()){ Node top = queue.remove(); System.out.println(top.value); if(top.left != null) queue.add(top.left); if(top.right != null) queue.add(top.right); } }
2.用棧實現隊列
import java.util.Stack; public class Queue { private Stack<Integer> stack1 = new Stack<>(); private Stack<Integer> stack2 = new Stack<>(); public void push(Integer value){ if(value == null){ throw new RuntimeException("value is null"); } stack1.push(value); } public Integer pop(){ if(stack1.size() == 0){ return null; } while(!stack1.empty()){ stack2.push(stack1.pop()); } Integer value = stack2.pop(); while(!stack2.empty()){ stack1.push(stack2.pop()); } return value; } public static void main(String[] args) { Queue queue = new Queue(); queue.push(1); queue.push(3); queue.push(5); queue.pop(); queue.pop(); queue.pop(); } }
3.包括max函數的棧
import java.util.Stack; public class StackMax { private Stack<Integer> stack = new Stack<>(); private Stack<Integer> s = new Stack<>(); public void push(Integer value) { stack.push(value); if (s.size() == 0 || value >= s.peek()) { s.push(value); } } public Integer pop() { Integer value = stack.pop(); if(value.equals(s.peek())){ s.pop(); } return value; } public Integer max() { return s.peek(); } public static void main(String[] args) { StackMax stackMax = new StackMax(); stackMax.push(1); System.out.println(stackMax.max()); stackMax.push(3); System.out.println(stackMax.max()); stackMax.push(4); System.out.println(stackMax.max()); stackMax.push(2); System.out.println(stackMax.max()); stackMax.pop(); System.out.println(stackMax.max()); stackMax.pop(); System.out.println(stackMax.max()); stackMax.pop(); System.out.println(stackMax.max()); } }
4.找一個n*n矩陣的最長上升序列
5.快速排序,何時複雜度最大
public void quickSort(int[] num, int st, int ed) { if (st >= ed) return; int left = st; int right = ed; int value = num[left]; while (left < right) { while(left < right && num[right] >= value){ right--; } num[left] = num[right]; while(left < right && num[left] < value){ left++; } num[right] = num[left]; } int mid = left; num[mid] = value; quickSort(num, st, mid - 1); quickSort(num, mid + 1, ed); } public static void main(String[] args) { QuickSort quickSort = new QuickSort(); int[] num = {3, 7, 4, 2, 5, 8, 1}; quickSort.quickSort(num, 0, 6); for (int t : num) { System.out.println(t); } }
6.歸併排序
import java.util.Arrays; public class MergeSort { public int[] mergeSort(int[] num) { if (num.length <= 1) return num; int mid = num.length / 2; int[] left = Arrays.copyOfRange(num, 0, mid); int[] right = Arrays.copyOfRange(num, mid, num.length); return mergeArrays(mergeSort(left), mergeSort(right)); } private int[] mergeArrays(int[] mergeSort1, int[] mergeSort2) { int[] result = new int[mergeSort1.length + mergeSort2.length]; int i = 0, j = 0, k = 0; while (i < mergeSort1.length && j < mergeSort2.length) { if (mergeSort1[i] < mergeSort2[j]) { result[k++] = mergeSort1[i++]; } else { result[k++] = mergeSort2[j++]; } } while (i < mergeSort1.length) { result[k++] = mergeSort1[i++]; } while (j < mergeSort2.length) { result[k++] = mergeSort2[j++]; } return result; } public static void main(String[] args) { MergeSort mergeSort = new MergeSort(); int[] num = {3, 7, 4, 2, 5, 8, 1}; num = mergeSort.mergeSort(num); for (int t : num) { System.out.println(t); } } }
7.手寫一個LRU
8.給你一個數組,數組長度爲n。請找出數組中第k大的數
public class Solution { public int findK(int[] num, int k) { return quickSort(num, 0, num.length - 1, k - 1); } public int quickSort(int[] num, int st, int ed, int k) { if (st >= ed) return num[st]; int value = num[st]; int left = st; int right = ed; while (left < right) { while (left < right && num[right] >= value) { right--; } num[left] = num[right]; while (left < right && num[left] < value) { left++; } num[right] = num[left]; } num[left] = value; if (left == k) return value; else if (left < k) { return quickSort(num, left + 1, ed, k); } else { return quickSort(num, st, left, k); } } public static void main(String[] args) { Solution solution = new Solution(); int[] num = {1,8,8,7,4,1,5,1,5,7}; System.out.println(solution.findK(num, 1)); System.out.println(solution.findK(num, 2)); System.out.println(solution.findK(num, 3)); System.out.println(solution.findK(num, 4)); System.out.println(solution.findK(num, 5)); System.out.println(solution.findK(num, 6)); System.out.println(solution.findK(num, 7)); System.out.println(solution.findK(num, 8)); System.out.println(solution.findK(num, 9)); System.out.println(solution.findK(num, 10)); } }
附加條件:不容許改變元素在數組中的位置
在int範圍內去中位數,算出其在數組中是第幾大的元素(遍歷數組O(n)),與k比較不斷二分。
9.找到數據流中的中位數
使用大小頂堆,若是放入的是奇數個,則取大頂堆的根結點,若是是偶數個則取大小頂堆根結點的平均值。
10.刪除鏈表中重複節點
HashSet
11.給定一個排序鏈表,刪除全部重複的元素,使得每一個元素只出現一次。
12.給定過一個二叉樹,原地將它展開爲鏈表
13.給定一個二叉樹,想象本身站在他的右側,按照從頂部到底部的順序,返回從右側所能看到的節點值。
11.判斷是不是二叉搜索樹
中序遍歷
12.合併兩個鏈表,用遞歸和非遞歸實現
13.字符串是否爲給定字符串的子串
14.查找兩個鏈表的公共節點
15.小頂堆
16.一個數x,一個數n,x中刪n位使得剩下的數最大
17.給定一顆二叉樹,求其中root的最長路徑。所謂路徑是指,聯通兩個節點的最小邊數。
18.二叉樹的序列化與反序列化