TreeSet/HashSet 區別
顧名思義,首先是結構上的不同
1、TreeSet背後的結構是TreeMap,也就是紅黑樹,能夠實現自動排序。它通過equals方法或者compareTo方法進行內容的比較。
2、HashSet背後是HashMap,key是無序的,只能做外部排序。既然是Hash,那麼就要重寫其中對象的hashCode和equals方法
另外,還有個細微的差別可以拿來裝b:
1、HashSet可以接受null值,有且只有一個
2、TreeSet默認不可以接受null值,會直接拋出空指針異常
set裏沒有重複數據,treeSet裏連虛無都沒有。
HashMap 如何解決衝突,擴容機制
爛大街的問題,問哪答哪吧。這樣的東西就是靠背。
HashMap的內部結構其實是數組+鏈表(java8後如果長度大於8則轉換爲紅黑樹)。HashMap初始化時,默認有16個hash槽。
存入對象時,首先,通過對象的hashCode,定位到hash槽。如果多個對象同時落入同一個槽,那麼就會使用鏈表解決本槽上的衝突。
HashMap在創建時,會有一個負載因子。每次put操作,都會檢查當前容量是否會超出閾值(initailCapacity*loadFactor)。如果超出,則擴容爲當前的兩倍。擴容後,數據需要重新散列,也就是transfer方法。
經驗:resize非常耗時,所以如果能夠提前預估容量,可以把initailCapacity提前固定下來。
ConcurrentHashMap 如何做到高併發的
簡單點說,使用了分段鎖(分離鎖)。每一把鎖用於鎖住容器中的一部分數據,減少線程間對鎖的競爭。
這道題往深裏問會死人的,篇幅有限,不囉嗦。
線程池平常怎麼用
普通的場景,使用工廠類Executors創建就可以了。常用的有Single、Fixed、Cached三種。
更多時候,爲了更精細的控制,會直接對ThreadPoolExecutor類進行定製。阿里的規範也要求這麼搞(當然要舔一舔),我尤其關心其中的阻塞隊列和飽和策略。
當然,你只有對阻塞隊列和拒絕策略熟悉才能這麼說。否則給自己挖坑就太不聰明瞭。
他們很喜歡你提到阿里規範,這讓我覺得jdk設計的很low
多個線程等待到某一節點然後統一放行有幾種實現方式?
最經典的就是CountDownLatch,主線程阻塞在await方法,每個線程調用countDown。可以解決一些經典的賽馬問題。
還有一個變種就是CyclicBarrier。每個線程都阻塞在await方法,達到一定閾值集體放行。
另外還可以使用一些較初級的api,比如Thread的join方法。Future的get方法等。複雜不推薦。
也可以答sleep啊。有什麼問題麼?我用while等待一個變量也是可以的,但我爲什麼要這麼做?
數據庫索引結構
B+ Tree,爲了適應緩慢的磁盤而生的一種索引結構。必須保證按照索引的最左前綴查詢。
Hash 和HashMap類似,處理衝突的方式是鏈表
pg的索引結構就多了去了。Mysql這麼少怎麼感覺怪怪的?難道要我回答存儲引擎的區別?
select * from t where a=? and b>? order by c limit 0,100 如何加索引
知道這個就結論就行了=>
當order by 字段出現在where條件中時,纔會利用索引而無需排序操作。其他情況,order by不會出現排序操作。
按照最左原則,我可以創建 (a,b) 的索引。
什麼是聚簇索引和非聚簇索引
一個表只能有一個聚簇索引。主索引文件和數據文件爲同一份文件,默認的InnoDB就支持聚簇索引,B+ Tree的葉子節點上的data就是數據本身。
而MyISAM就不支持聚簇索引,它的葉子結點存放的不是數據本身,而是數據存放的地址。在文件結構上,會分爲一個索引文件、一個數據文件。
對編程來說沒什麼鳥用。
瞭解 CAP 嗎?redis 裏的 CAP 是怎樣的?
- Consistency 一致性
- Availability 可用性
- Partition tolerance 分區容錯
- 一般,都在C、A之間進行權衡。
redis簡單主從模式側重於CP的,即對於一致性要求較高。
redis-cluster,則屬於AP類型,更加強調可用性
cap就是帽子,綠油油的那種
如何理解冪等?項目中接口的冪等是如何做的?
冪等是指多次執行,影響相同。
比如大多數Post操作,重複提交訂單等,最終只會有一個訂單生成成功。還有一種情況就是消息,由於大多數MQ之保證at least once,所以消息有時會重複。
1、對於Post請求,我一般在請求成功後,強制跳轉到其他頁面,避免刷新提交。
2、複雜的操作一般使用流水號來實現。
3、某些不帶流水號的消息,處理的時候,就要進行多次校驗和check,甚至引入消息狀態表,來保證冪等。
就如同表白,每次表白都是被拒絕,因爲我就是那個id!
解釋下樂觀鎖悲觀鎖
悲觀鎖總是假設情況最壞,每次操作數據都認爲別人會修改,就加鎖來保證安全。後面的訪問者只能等待。數據庫中的行鎖、表鎖,java中的同步關鍵字等,都屬於悲觀鎖。
樂觀鎖正好相反,總是假設最好的情況,不用對數據加鎖,但多了一次額外的判斷操作。比如concurrent包裏大量的CAS操作、判斷新舊版本號機制等。
悲觀鎖是老婆,有你獨佔;樂觀鎖是炮友,按預約規劃
JVM 判斷對象是否回收?
答案就是GC roots。也就是從根對象出發,沒有任何一個對象引用到它,那麼就判斷這個對象是不可達的。
通常被罵“斷子絕孫”的人,就是要被回收的root
GCROOT 有哪些?
1 、 虛擬機棧(棧幀中的本地變量表)中引用的對象。
2、 本地方法棧中JNI(即一般說的native方法)引用的對象。
3、 方法區中的靜態變量和常量引用的對象。
4、活躍線程的引用對象
所以不要讓他們過度繁殖。
反射能獲得類裏面方法的名稱嗎?參數名稱呢?參數類型呢?
都可以。
java8以後,通過Parameter類獲取參數名稱。但有前提,需要加編譯開關。
javac -parameters
默認是關閉的,幹!
問題都偏到月球上去了
動態代理的實現方式?CgLib 和 jdk 的代理有什麼區別?
java中通過實現InvocationHandler接口來實現動態代理,然後使用Proxy將其初始化。
Cglib使用了ASM自己嗎生成框架,可以代理普通類,但代理不了final類,而jdk的只能代理接口。
在spring裏,cglib勝出
分佈式鎖有哪些主流實現方式?redis 和 zk 鎖有什麼區別?
大體分爲兩類。
樂觀鎖:
基於版本號機制和CAS實現,與存放版本號的存儲無關。
悲觀鎖:
1、基於數據庫記錄,進入時寫數據,退出時刪記錄
2、數據庫行鎖,比如分佈式quartz,它是一把排它鎖
3、基於Redis的setnx函數(由於大多數會設置超時,所以推薦用帶px的set原子函數)
4、基於zookeeper
區別:
redis獲取鎖是輪訓機制。鎖釋放後會有多個調用者爭搶,某些任務有可能餓死。
zk是監聽機制,有變動會接到通知。除了非公平鎖,也可以實現公平鎖。
從優雅性來說,顯然redis勝出
ThreadLocal 作用是什麼?說下用法
ThreadLocal用來隔離數據。
ThreadLocal中存放的是與線程相關的數據,底層實際上是一個map,通過線程可以獲取存儲數據的map。
這種方式與Servlet中的Request類似。
一些需要綁定到線程的數據,比如一些線程的統計數據,就可以放在這裏。
據說這是一種線程同步方式,但它明顯無鎖啊。
ThreadLocal有沒有優化方式?
ThreadLocal中的Map性能較差,解決Hash採用的線性探測方法。
Netty就對它進行了優化,優化方式是繼承了Thread類,實現了自己的FastThreadLocal。它使用
搞不懂jdk,明明有O(1)的Map,非要自己造個更慢的輪子,爲什麼呢?話說,這個問題,簡直又偏到火星了。
設計秒殺系統要考慮哪些點?
1、數據預熱 秒殺都是瞬時操作,不要等流量來了再加載數據。可以提前對數據進行預熱,比如加載到緩存等。
2、緩存 包括CDN緩存和數據緩存。保證緩存系統的高可用,數據隨後落地。
3、解決超賣 引入MQ,串行化操作庫存,達到閾值後不再消費,並關閉購買功能。或者直接操作緩存。
4、流量削峯 通過引入MQ,將耗時業務進行削峯,平穩處理用戶需求。
5、熔斷限流 熔斷,優先保證主要業務的進行。限流,識別異常流量,進行封鎖;同時,允許部分請求失敗。
6、彈性擴容 在判斷系統負載達到極限時,可以通過增加服務器的途徑抵抗峯值。需要打通運維環境,能夠快速擴容。
怕就怕抓住一點問到底。秒殺個屁啊,淘寶的秒殺要麼搶不到,要麼500!
我面試的資料總結到了這篇文章:180+道Java面試題目!含答案解析!