面经-北京小厂

2023-08-04 15:08:04 浏览数 (1)

这篇文章记录我的面经—北京小厂

介绍下你吧

代码语言:javascript复制
从大一下学期开始学java,自己从网上找项目做,一周深挖一个方面的知识,广度方面-分布式这些都有了解
深度方面都有一些基础吧,就是这些。

是不是简历上的在线教育平台啊?是你一个人做的还是

代码语言:javascript复制
这是一个学长的公司带我一块儿做的  然后没工资,我只是帮忙(笑着说嘿嘿)去学习

在里面都做了什么

代码语言:javascript复制
比如下单,用rabbit搭建延迟消息这些搭建和设计,他们带我一起学习,有些东西是我去实现的,
一起完善这些落地的东西。

你在项目中解决哪些有代表性的问题?

代码语言:javascript复制
您是说是比较难的还是些小bug

都可以,你感觉有代表性的,咱们可以聊一聊,

就是下单的时候,不是要往task表插入记录嘛,然后需要通过token去实现放重提交对吧
然后他是通过分布式锁来完成存在删除,保证它多线程下的原子性,但是他这样放重提交,
不能保证有些恶意用户,恶意竞争的商家通过轮询的方式,点一次获取,获取一次,虽然
库存会过一段时间,比如半个小时会释放,但是他这样恶意刷的话,比如100个库存,1个人全部刷完
这种问题,对吧,这里解决当时也是想了好久(笑的很大声) 
有两种策略,一个是限流策略,比如一个用户一分钟只能下一次单这种
第二个就是redis预扣减,当然这里的话,因为是之前的项目,现在才想到这样解决
就是redis预扣减,先在redis预扣减,等支付再进行扣减,就不会有恶意扣库存的问题了
这是我感觉最近学习最深入的

预扣减和实际去扣减去扣减,怎么去同步?

代码语言:javascript复制
这里我还没深入,是我学习的另一个项目,还没落地到这里,就是知道预扣减的好处

=========================================================
这里一直拖  被八股托太久了 马上就去复盘明白这个redis预扣减

平时在java中,数组和链表如何取舍的?

代码语言:javascript复制
增加和删除用链表,改查用数组
因为链表是一个连一个的所以用链表,增加和删除会快一点
为啥修改和查看用数组
因为如果要删除一个数组的话,比如我要把那个2删掉,我要把后面的元素整体的移动一下
然后改查的话,数组遍历每一个下标 ,他是O(1级别的,而链表的话需要一个一个遍历
=============================================================
这里我又了解到,甚至是linkedList的作者都不会去使用链表,
因为即使他增加删除快一点,但是他增加删除需要先找到吧,这是对比数组的常数操作,O(1)是很浪费时间的

有没有办法,提高链表的查询速度?

代码语言:javascript复制
双向链表吧,这样可能会快一点
  
 面试官:那也是,你双向,一个从头一个从尾部,实际没改变
 
 
 这里没了解过
 
 ============================================================================
 这里真的傻了  好气  不过吃一堑长一智  下次知道就行了
 有一个我学过 就是给链表加索引 也就是redis中用的跳表 用二分的思想,但是前提要保证链表有序
 
 LRU问题,在链表的基础上加一个hashmap
 这里推荐这篇文章

[]: https://blog.csdn.net/qq_46489085/article/details/124933345

我们如何比较字符串是否相等呢?

代码语言:javascript复制
用equals

为什么不用其他的
等于比较的是地址,equlas比较的是堆里的内容

我们平时也会用到,加入程序里要实现乐观锁,你会怎么做?

代码语言:javascript复制
乐观锁,用一个标识a,另一个我们并发访问的时候可以和他比较,一个线程不符合的话会直接失败
然后就是通过version解决乐观锁的ABA问题,
乐观锁我没在java里实现过,是在超发那里用的乐观锁
直接用的库存大于0这种,这样的乐观锁,这样的粒度比加版本号的小一点
乐观锁的本质,通过自旋和比较,怎么说,通过 emm,默认是没有竞争,假设没竞争这样去做 


面试官:在数据库里面加乐观锁吗?
在sql,扣减嘛  update  stock = stock-1  然后stock>0

===============================================================================
乐观锁在java中常用的cas(compare and swap)顾名思义先比较再替换,
(这里我列几个概念,大家自习了解,也是我之前混淆的,JUCCASAQS,CAS是AQS的基础,AQS是JUC的基础,
所以可以说CAS也是JUC的基础,CAS是非阻塞的算法,synchronized是阻塞的算法,需要注意的是juc可以实现乐观锁和悲观锁)
我们可以利用juc提供的原子类去在程序中实现乐观锁,(Atomicxx类和其中的方法如compareAndSet都是基于CAS算法实现的
(juc包括原子类、工具类和一些接口)Lock接口和他的实现类可以
实现悲观锁(ReentranLock
还有一点需要说明:CAS主要干两件事:锁自旋和乐观锁
所以可以这样回答:java中,可以使用juc提供的原子类和方法实现cas算法,从而实现乐观锁
(也可以把AtomicLong换成普通的Long,但是就必须手动实现一个cas自增的算法了)
如下面例子:
public class User {
    private Long id;
    private String name;
    private Integer age;
    private AtomicLong version;

    // getter 和 setter 方法省略

    // 更新 User 数据
    public void update(User newData) {
        // 读取数据库中的原始数据
        User oldData = userDao.getById(newData.getId());
        // 判断版本号是否相等
        if (oldData.getVersion().get() != newData.getVersion().get()) {
            throw new OptimisticLockException("数据被其他线程修改");
        }
        // 更新数据并增加版本号
        newData.getVersion().incrementAndGet();
        userDao.update(newData);
    }
}

这里再拓展一下:
用时间戳实现乐观锁并不需要原子类,因为时间戳一般都是通过对当前时间的获取来实现的,而对
    时间的获取是线程安全的。

以 User 示例为例,如果我们使用时间戳来实现乐观锁,可以将版本号 version 改为 timestamp
时间戳(毫秒数),然后在每次更新数据时,先从数据库读取旧数据,判断旧数据的 timestamp 是否
等于当前 timestamp,若等于则进行更新操作,否则抛出乐观锁异常。这种实现方式不需要手动实现 
CAS 算法,也不需要使用原子类,代码比较简单:
public class User {
    private Long id;
    private String name;
    private Integer age;
    private long timestamp;

    // getter 和 setter 方法省略

    // 更新 User 数据
    public void update(User newData) {
        // 读取数据库中的原始数据
        User oldData = userDao.getById(newData.getId());
        // 判断时间戳是否相等
        if (oldData.getTimestamp() != newData.getTimestamp()) {
            throw new OptimisticLockException("数据被其他线程修改");
        }
        // 更新时间戳并保存到数据库
        newData.setTimestamp(System.currentTimeMillis());
        userDao.update(newData);
    }
}

总结:
    面试如果可以这样说,一种是通过版本号比较,利用juc提供的原子类和方法,实现cas算法,从而实现乐观锁,
    还有一种就是基于时间戳,时间戳的获取没用并发安全问题,可以不借助原子类,

想深入学习乐观锁的伙伴可以看这篇文章

[]: http://www.manongjc.com/detail/61-jnkzpokxmwhpxul.html

那你对0拷贝做过了解吗

代码语言:javascript复制
这里看过,但是每记录嘿嘿,当时先把深拷贝和浅拷贝学习了下,然后直接去忘记深入这个了嘿嘿
=====================================================
这里是操作系统方面的知识,
通过各种方式,在特殊情况下,减少数据的拷贝次数或者说减少cpu参与数据拷贝的次数。

我们用多线程,生产者消费者这种场景的话,平时怎么去做的

代码语言:javascript复制
生产中,我知道的是线程池,不可以去Executors工具类去做,
生产中要自定义,用ThreadPoolExector
这里的阻塞队列要区分C端和B端,如果C端高并发请求下,
他的阻塞队列不可以太长,会有消息的堆积
B端的话要求,不要那么强的实时性,可以让阻塞的队列长一点
然后核心线程数要分io密集型还是cpu密集型,cpu密集型的话可以和cpu核数一致或者加1
io密集型,对cpu没那么高的要求,可以2N 1
然后ThreadPoolExector实现的话主要是核心线程数,最大线程数,拒绝策略,阻塞队列长度
当然拒绝策略使用默认的那个就行
然后这就是我知道的生产消费模型

用这个到项目上面,堆内存怎么配?

代码语言:javascript复制
您能说的 精确点吗
就是线程池,我们使用的时候,会设计jvm的配置,这里的话会怎么去考虑?
这里的话,线程池的底层是一个hashSet,emmm,这里不会

面试官:比如,线程栈,默认的线程栈的大小,
没去了解过这里...
====================
这里我觉得面试官问的有点问题,jvm调优,应该告诉我一个确定的场景什么的

tcp连接熟吗

代码语言:javascript复制
这个,他这个计算机网络,这里太多了 学习了 但是想不起来嘿嘿
太多了呜
面试:他有几次挥手啊

三次握手四次回收这样

面试官,假如说,挥手 的时候,有个状态叫closewait,这是什么原因产生的
emm忘记了呜

你是springboot熟还是alibabacloud熟吗

代码语言:javascript复制
boot吧
面试官:拦截器这里怎么做,平时用过没?
拦截器,Intercepor,比如我用jwt实现登录的时候,就需要拦截请求,做出处理
这就是拦截器,我在这儿用的,

面试官:拦截器加载的顺序?
这里没了解
面试官:就是扩展一个接口,扩展一个类
(这里真蒙了,可能是有人进来了
我蒙了个,::就是实现一个类,重写Interpcptor方法,(这里我真是蒙的嘿嘿  
我补了一句,主要是思想吗,api啥的记不住,用的时候看一下就可以了
===========================================================================
这里说的扩展一个类,就应该是我项目里的LoginInterceptor咋写的
我回顾了下,就是实现HandlerInterceptor接口,重写preHandle和postHandle,和afterCompletion方法
这是添加一个拦截器,
然后每个微服务要注册拦截器,这里需要实现WebMvcConfigurer
重写addInterceptors()方法,registry.addInterceptor去添加自己定义滴拦截器。

然后拦截器的加载顺序:
(下面的就是列举一下,我感觉没必要背会。。)
在 Spring Boot 中,拦截器是通过实现 HandlerInterceptor 接口来定义的。当请求过来时,Spring Boot 会使用一个拦截器链来依次对请求进行拦截和处理,拦截器的加载顺序是与它们在代码中注册的顺序相关的。

具体来说,在 Spring Boot 中,可以通过实现 WebMvcConfigurer 接口,并重写 addInterceptors 方法来注册拦截器。在该方法中,可以使用 addInterceptor 方法来注册拦截器,并使用 order 方法来指定拦截器的优先级。order 数值越小,优先级越高,即越先被调用。

默认情况下,Spring Boot 实现的拦截器链中包含以下几个拦截器(按照执行顺序排序):

ResourceUrlProviderExposingInterceptor
LocaleChangeInterceptor
ThemeChangeInterceptor
SecurityContextPersistenceFilter
HeaderWriterFilter
CsrfFilter
LogoutFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
如果我们在代码中什么都不做,那么这些默认的拦截器就会按照上述顺序被依次执行。如果我们需要自己定义拦截器,可以通过实现 HandlerInterceptor 接口,并在 WebMvcConfigurer 中的 addInterceptors 方法中添加我们自己的拦截器并指定其 order 值,从而改变拦截器执行的顺序。

面向对象里面有重载,对一个方法重载怎么实现

代码语言:javascript复制
就是把形式参数改一下就是重载

大概情况我们就了解这么多,对我们公司有想知道的吗

代码语言:javascript复制
公司的规模大概多大
面试官:我们公司有差不多400号人
我:属于小中厂是吗
面试官:小厂是吗

你在哪个地方

代码语言:javascript复制
吉林
面试官:我们的公司在北京,有什么影响吗
我:大三也没啥课
准备暑期之后,上课的话也可以找人代课吗,实习挺重要的
面试官:所以可以在北京一直呆着是吗
对

0 人点赞