目錄
?昨日總結
今日計劃
算法——兩個數組的交集
算法——兩數之和
緩存穿透
常見解決方案
緩存雪崩
常見解決方案
緩存擊穿
常見解決方案
棧溢出
堆溢出
功能接口式參數&泛型函數
?編輯
?昨日總結
- 緩存問題完結(緩存穿透、雪崩、擊穿),了解堆、棧溢出,功能接口式參數&泛型函數,完成點評緩存部分
- cv(停滯中)
- 背誦小林coding--Java并發面試篇(1/3)
- 代碼隨想錄——兩個數組的交集,兩數之和
今日計劃
- 跟進點評項目
- cv(停滯中)
- 背誦小林coding--Java并發面試篇(1/3)
- 代碼隨想錄——四數相加,三數之和
算法——兩個數組的交集
給定兩個數組?nums1
?和?nums2
?,返回?它們的?交集?。輸出結果中的每個元素一定是?唯一?的。我們可以?不考慮輸出結果的順序?。
示例 1:
輸入:nums1 = [1,2,2,1], nums2 = [2,2] 輸出:[2]
示例 2:
輸入:nums1 = [4,9,5], nums2 = [9,4,9,8,4] 輸出:[9,4] 解釋:[4,9] 也是可通過的
class Solution {public int[] intersection(int[] nums1, int[] nums2) {Set<Integer> set = new HashSet<>();for(int num : nums1) {set.add(num);}Set<Integer> T = new HashSet<>();for(int num : nums2) {if(set.contains(num))T.add(num);}//通過 stream() 方法將 List 轉換為流,mapToInt 方法將 Integer 類型的流//轉換為 int 類型的流, 最后使用 toArray 方法將流轉換為 int 數組。int[] t = T.stream().mapToInt(Integer::intValue).toArray();return t;}
}
算法——兩數之和
給定一個整數數組?nums
?和一個整數目標值?target
,請你在該數組中找出?和為目標值?target
? 的那?兩個?整數,并返回它們的數組下標。
你可以假設每種輸入只會對應一個答案,并且你不能使用兩次相同的元素。
你可以按任意順序返回答案。
示例 1:
輸入:nums = [2,7,11,15], target = 9 輸出:[0,1] 解釋:因為 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
輸入:nums = [3,2,4], target = 6 輸出:[1,2]
class Solution {public int[] twoSum(int[] nums, int target) {Map<Integer,Integer> map = new HashMap<>();for(int i = 0; i< nums.length; i++) {int temp = target - nums[i];if(map.containsKey(temp))return new int[] {map.get(temp),i};map.put(nums[i],i);}return new int[0];}
}
緩存穿透
緩存穿透是指客戶端請求的數據在緩存中和數據庫中都不存在,這樣緩存永遠不會生效,這些請求都會打到數據庫。
常見解決方案
- 緩存空對象:當客戶端訪問redis未命中時,會訪問數據庫 ,若此時數據庫也未命中,將會redis緩存一個null空對象
優點:實現簡單,維護方便
缺點 :額外的內存消耗,可能造成 短期的不一致(針對此問題,1.可以為緩沖空數據設置一個TTL生存時間 2.可以每完成一次數據庫的操作 ,便立即更新緩存 )
- 布隆過濾
當客戶端發出 請求 時 ,會首先經過布隆過濾器進行判斷,是否 存在該數據 。若存在之后在經過redis或再經過數據庫。
布隆過濾器原理 :用二進制位和哈希函數來存在 該數據是否存在
優點 :內存占用較小,沒有多余key
缺點:實現 復雜 ,存在誤判可能
緩存雪崩
緩存雪崩是指在同一時段大量的緩存key同時失效或者Redis服務宕機,導致大量請求到達數據庫,帶來巨大壓力。
常見解決方案
- 給不同的Key的TTL添加隨機值
- 利用Redis集群提高服務的可用性
- 給緩存業務添加降級限流策略
- 給業務添加多級緩存
緩存擊穿
緩存擊穿問題也叫熱點Key問題,就是一個被高并發訪問并且緩存重建業務較復雜的key突然失效了,無數的請求訪問會在瞬間給數據庫帶來巨大的沖擊。
常見解決方案
- 互斥鎖
優點:沒有額外的內存消耗 ,保證 一致性,實現簡單
缺點:縣城 需要等待,性能受影響,可能會出現死鎖風險
- 邏輯過期
優點:線程無需等待 ,性能較好
缺點:不保證一致性,有額外的內存消耗,實現復雜。
棧溢出
在Java中,棧是線程私有的,主要用于存儲局部變量和方法調用等。每個線程創建時都會分配一定大小的棧空間,當空間被分配完時,便會發出棧溢出。一般出現棧溢出的情況如下:
- 陷入死循環的遞歸調用
如果遞歸方法沒有正確的終止條件或者終止條件設置不合理,會導致方法不斷地調用自身,棧幀不斷入棧,最終耗盡棧空間。
- 方法調用的層次過深
在一個嵌套很深的方法調用中,每個方法都會在棧上分配一定的空間,當調用層次過深時,棧空間就會被耗盡。
- 局部變量占用的空間過大
如果在方法中定義了大量的局部變量,尤其是一些占用空間較大的數組或對象,會增加每個棧幀的大小,從而使棧空間更快地被耗盡。
堆溢出
在Java中,堆是線程共享的內存區域,主要存儲對象的實例。當空間無法為新的對象分配內存時,就會發生堆溢出。一般出現堆溢出的情況如下:
- 創建的大量對象沒有及時回收
- 內存泄漏
內存泄漏是指程序中某些對象不再使用,但由于存在引用關系,垃圾回收器無法回收這些對象,導致堆空間不斷被占用。例如靜態集合類中的對象不會被垃圾回收、某些資源的連接未關閉,造成資源泄漏
- 分配的堆空間過小
如果Java虛擬機(JVM)的堆空間設置過小,無法滿足程序運行時的內存需求。
功能接口式參數&泛型函數
函數傳遞功能參數通常指的是將行為(函數)作為參數傳遞給另一個方法。
在寫工具類時,如果返回類型不確定,要 利用泛型來解決,讓調用者去告訴具體的函數類型
例如:當在一個工具類中調用數據庫函數時,需要通過函數自身傳遞的參數來傳入,因此通過Lambda表達式傳遞功能
public <R, ID> R queryWithPassThrough(String keyPrefix, ID id , Class<R> type, Function<ID, R> dbFallback,Long time, TimeUnit unit) {String key = "cache:shop:" + id;//從redis查詢商鋪緩存String json = stringRedisTemplate.opsForValue().get(key);//判斷緩存是否存在if (StrUtil.isNotBlank(json)) {//存在 直接返回return JSONUtil.toBean(json, type);}//判斷命中的是否是空值if(json != null) {//返回一個錯誤信息return null;}//不存在,根據id查詢數據庫R r = dbFallback.apply(id);//數據庫不存在返回錯誤if (r == null) {//將空值寫入redisstringRedisTemplate.opsForValue().set(key,"",2L,TimeUnit.MINUTES);return null;}//數據庫存在,寫入redisthis.set(key,r,time,unit);//返回return r;
}
?this::getById?是一個實例方法引用,它等價于Lambda表達式:id -> this.getById(id);
public Result queryById(Long id) {Shop shop = cacheClient.queryWithPassThrough(CACHE_SHOP_KEY,id, Shop.class,this::getById,CACHE_SHOP_TTL,TimeUnit.MINUTES);return Result.ok(shop);
}