Java,八股,cv,算法——双非研0四修之路day16
目录
昨日总结
今日计划
算法——两个数组的交集
算法——两数之和
缓存穿透
常见解决方案
缓存雪崩
常见解决方案
缓存击穿
常见解决方案
栈溢出
堆溢出
功能接口式参数&泛型函数
编辑
昨日总结
- 缓存问题完结(缓存穿透、雪崩、击穿),了解堆、栈溢出,功能接口式参数&泛型函数,完成点评缓存部分
- 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);
}