面经记录复盘
把第一段实习删了。。精心准备了项目,结果也不问项目还是只问实习。。即使实习做的点都很小有的还有包装
- 为啥用threadlocal ?其实是想让我说线程安全
- threadlocal原理
- 乐观锁一定比悲观锁性能高么?不一定吧,高并发下乐观锁也是只有一个线程能抢到锁,其他线程都失败了,失败的会在那自旋等待?这种情况比悲观锁性能差,因为synchronized在Java中经过优化,因为它会将线程挂起,减少CPU空转
- CAS原理: 比较并交换,会比较JMM共享内存里的值和自己预期值,一致的话才更新,不一致说明有其他线程更改后,会自旋
- ABA问题 如何解决
- token+redis幂等,对token加分布式锁,超过锁的ttl会怎么样? 加分布式锁5秒,set nx 同一时刻只允许一个线程且先判断是否存在:它保证了即使有多个相同
requestId
的请求同时到来,也只有一个线程能抢到锁并执行后续逻辑。5秒后,锁过期??我说的是过期也没事,这只是第一层防护,分布式锁击穿后,多个线程同时查缓存,同时创建订单,会。。。最后还有“数据库唯一索引”和“状态机”限制。
> 看门狗机制在Redisson中确实存在,它会在锁即将过期时自动续期。这个机制默认在未显式指定超时时间时生效,通过后台线程定期检查并重置锁的过期时间。不过如果用户手动设置了锁超时时间,看门狗就会失效
- rag是不是自己做的 说是公司中间件有提供,就没问了。。但我也了解rag流程,了解向量数据库milvus是咋咋咋
- 只做单机限流,万一有几台机子挂了,流量全打到另外机子上。。
- 多级缓存一致性如何保障
- 为啥用责任链?你这个场景还可以用什么模式?没回答出来策略模式,追问策略模式是什么
- 实现一个cache 不指定其大小且不发生OOM 重点是软引用,软引用如何回收
- 为啥用流式SSE 相比阻塞的好处和坏处?阻塞是主线程会阻塞等待执行完,会等1分钟不友好,flux是非阻塞的异步的 直接返回,流式send,能够发出 0 到 N 个元素的序列流
- 手撕:滑动窗口最大值 线程顺次打印AB
ThreadLocal
提供了线程局部的变量。每个访问该变量的线程都有自己的、独立初始化的变量副本。
- 原理:在 Thread 类中,有一个私有成员 threadLocals,其类型是 ThreadLocalMap。ThreadLocalMap 是一个定制化的哈希表,其**Key是ThreadLocal对象本身**(弱引用),Value是你设置的值。当你调用 threadLocal.set(value) 时,实际上是在**当前线程的threadLocals这个Map里,以当前threadLocal对象为Key,设置了Value**。当你调用 threadLocal.get() 时,是从当前线程的threadLocals这个Map里,用当前threadLocal对象作为Key去取值。
- 结论:数据是存储在每个线程对象内部的,所以不同线程之间天然隔离,不存在线程安全问题。
CAS(Compare-And-Swap)是一种原子操作,用于在多线程环境下实现同步。它包含三个操作数:内存位置(V)、预期原值(A)和新值(B)。CAS的操作过程如下:
- 比较内存位置V的值与预期原值A。
- 如果相等,则将内存位置V的值修改为新值B。
- 如果不相等,则不做任何操作,或者重试(自旋)。
CAS操作是原子的,这意味着在操作过程中不会被打断。现代处理器通常支持CAS指令(例如,x86架构的CMPXCHG指令)。当多个线程同时尝试更新同一个变量时,只有一个线程能成功更新,其他线程会失败。失败的线程通常会重试(即自旋),不断尝试CAS操作,直到成功为止。这种自旋的行为称为自旋锁。
乐观锁和悲观锁性能比较
乐观锁和悲观锁的性能优劣取决于并发场景:
- 竞争激烈(高并发):当多个线程竞争同一资源时,悲观锁可能会导致线程阻塞,而乐观锁会导致大量自旋,消耗CPU资源。在这种情况下,悲观锁(如synchronized)在Java中经过优化,可能性能更好,因为它会将线程挂起,减少CPU空转。而乐观锁的自旋会占用CPU,导致性能下降。
- 竞争不激烈(低并发):在大多数情况下,乐观锁的性能优于悲观锁,因为加锁和解锁操作需要开销,而乐观锁避免了这些开销。而且,在没有竞争的情况下,CAS操作成功率高,自旋次数少。
因此,并不能绝对地说乐观锁一定比悲观锁性能高,它取决于具体的应用场景。
选择标准
- 冲突概率:<20% 选乐观锁,>30% 考虑悲观锁
- 临界区大小:操作简单用乐观锁,复杂操作用悲观锁
- 响应时间要求:低延迟用乐观锁,可接受阻塞用悲观锁
所以 秒杀既不能用悲观锁也不用乐观锁
(面试下来想到了。。。qisi)
SSE好处:
- 实时性更好:SSE是建立在HTTP之上的持久连接,服务器可以立即发送数据,而无需等待客户端请求。
- 减少网络开销:SSE使用一个长连接,避免了客户端反复建立连接的开销(如HTTP头部重复传输)。
- 简单易用:SSE使用简单的文本协议,前端可以使用EventSource API轻松处理。
- 自动重连:EventSource API支持自动重连,在连接断开时会尝试重新连接。
SSE坏处:
- 相比MCP Steamable http,刷新的话不支持续传
- 不支持双向传输,浏览器开启sse有限制