中通 JAVA软件开发 一面 面经

1、介绍一下你的实习项目经历

我在上一家公司实习期间主要负责物流管理系统的开发工作。这个系统主要处理订单流转、仓储管理、配送调度等核心业务,日均处理订单量在20万左右。

我主要参与了三个模块的开发:1)订单管理模块,负责订单的创建、状态流转、异常处理等功能,用到了状态机模式来管理订单的生命周期;2)库存管理模块,实现了库存的实时扣减和预占功能,通过Redis分布式锁保证并发安全,用消息队列做异步库存同步;3)数据报表模块,基于ElasticSearch做订单数据的聚合分析,生成各种维度的统计报表。

在这个过程中遇到过一些技术挑战,比如高峰期接口响应慢的问题,我通过添加Redis缓存、优化SQL查询、引入本地缓存等方式,把接口响应时间从500ms优化到了80ms左右。还遇到过分布式事务的问题,订单创建成功但库存扣减失败,我引入了本地消息表+定时补偿的方案来保证最终一致性。

这段实习经历让我对分布式系统有了更深的理解,也积累了解决实际问题的经验。

2、HashMap和ConcurrentHashMap在设计上有什么区别

这两个Map在设计上的核心区别是线程安全性和并发性能:

  1. 线程安全方面,HashMap是非线程安全的,多线程环境下可能出现数据丢失、死循环等问题。ConcurrentHashMap是线程安全的,采用了分段锁和CAS机制来保证并发安全。
  2. 数据结构方面,HashMap是数组+链表+红黑树的结构。ConcurrentHashMap在JDK7用的是Segment分段锁,每个Segment是一个小的HashMap;JDK8放弃了Segment,改用Node数组+链表+红黑树,锁的粒度更细化到每个数组位置。
  3. 并发控制方面,ConcurrentHashMap的put操作,如果数组位置为空就用CAS插入,如果有冲突就用synchronized锁住链表或红黑树的头节点。这样不同位置的操作可以并发执行,大大提升了并发度。
  4. 扩容机制方面,HashMap扩容时会阻塞所有操作。ConcurrentHashMap支持多线程协助扩容,正在扩容时其他线程可以帮忙迁移数据,提升扩容效率。
  5. size统计方面,HashMap直接返回size变量。ConcurrentHashMap用baseCount加上counterCells数组来统计,减少了并发竞争。

实际使用中,单线程或明确不会并发访问用HashMap性能更好,多线程环境必须用ConcurrentHashMap保证安全。

3、线程池的工作原理是什么,核心线程空闲时是什么状态

线程池的工作原理可以分几个层次来说:

  1. 核心参数包括corePoolSize核心线程数、maximumPoolSize最大线程数、keepAliveTime空闲时间、workQueue任务队列、threadFactory线程工厂、handler拒绝策略。
  2. 任务提交流程是:首先判断核心线程数是否已满,没满就创建新线程执行任务;核心线程满了就把任务放入队列;队列满了就创建非核心线程;线程数达到最大值就执行拒绝策略。
  3. 线程复用机制是核心线程执行完任务后不会销毁,而是从队列中取新任务继续执行。具体实现是通过Worker对象的runWorker方法,里面有个while循环不断从队列取任务,取到就执行,取不到就阻塞等待。

关于核心线程空闲时的状态:

  1. 核心线程在没有任务时会调用workQueue.take()或poll()方法从队列获取任务,这时线程处于WAITING或TIMED_WAITING状态,不会占用CPU资源。
  2. 如果用的是LinkedBlockingQueue的take()方法,线程会一直阻塞等待,状态是WAITING。如果配置了allowCoreThreadTimeOut为true,会用poll(keepAliveTime)方法,超时后线程会被回收,状态是TIMED_WAITING。
  3. 默认情况下核心线程不会被回收,会一直保持在线程池中等待任务。非核心线程超过keepAliveTime没有任务就会被销毁。

我在项目中用线程池处理异步任务时,会根据业务特点设置合理的参数,比如IO密集型任务核心线程数设置为CPU核心数的2倍,CPU密集型设置为CPU核心数+1。

4、JVM有哪些核心参数和常用的垃圾回收器

JVM的核心参数主要分几类:

  1. 堆内存参数,-Xms设置初始堆大小,-Xmx设置最大堆大小,一般设置成一样避免动态扩容。-Xmn设置新生代大小,-XX:SurvivorRatio设置Eden和Survivor的比例,默认8:1:1。-XX:MaxMetaspaceSize设置元空间大小。
  2. GC相关参数,-XX:+UseG1GC指定使用G1收集器,-XX:MaxGCPauseMillis设置最大停顿时间,-XX:G1HeapRegionSize设置Region大小。-XX:+PrintGCDetails打印GC日志,-Xloggc指定日志文件路径。
  3. 其他参数,-Xss设置栈大小,默认1MB。-XX:+HeapDumpOnOutOfMemoryError在OOM时自动dump堆,-XX:HeapDumpPath指定dump文件路径。

常用的垃圾回收器有:

  1. Serial收集器,单线程收集,适合客户端应用或小内存场景,STW时间短但吞吐量低。
  2. Parallel收集器,多线程并行收集,注重吞吐量,适合后台计算任务。JDK8默认使用。
  3. CMS收集器,以最短停顿时间为目标,采用标记-清除算法,适合对响应时间要求高的应用。缺点是会产生内存碎片,CPU占用高。
  4. G1收集器,JDK9之后的默认收集器,把堆分成多个Region,可以预测停顿时间。适合大堆内存场景,兼顾吞吐量和停顿时间。我们生产环境用的就是G1。
  5. ZGC收集器,JDK11引入的低延迟收集器,停顿时间不超过10ms,适合超大堆内存和对延迟极其敏感的场景。

实际使用中要根据应用特点选择,一般推荐G1,它的表现比较均衡。

5、Stream流中map和flatMap有什么区别

map和flatMap都是Stream的中间操作,但处理方式不同:

  1. map是一对一映射,对流中每个元素应用函数,返回一个新元素。比如把List<String>转成List<Integer>,每个字符串映射成它的长度。返回的是Stream<T>。
  2. flatMap是一对多映射,对每个元素应用函数后返回一个流,然后把所有流合并成一个流。比如把List<List<String>>拍平成List<String>。返回的也是Stream<T>,但会把嵌套的流展开。
  3. 使用场景上,map用于简单的类型转换或字段提取。flatMap用于处理嵌套结构,比如一个订单包含多个商品,要获取所有商品列表就用flatMap。
  4. 举个例子,有个List<String>包含多个句子,要获取所有单词。用map会得到Stream<String[]>,每个句子split后是个数组。用flatMap配合A

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论

相关推荐

今天 00:04
已编辑
门头沟学院 Java
约面的挺突然。。狠下心接了1.自我介绍2.讲讲JAVA的反射3.可以继续讲讲AOP,动态代理[&nbsp;因为讲反射不小心吟唱到了例如AOP的动态代理,但是这块记忆的非常不熟,结果磕磕绊绊&nbsp;]4.项目我看你写了AOP和注解,具体怎么实现滑动窗口限流的[&nbsp;梦到什么说什么,吟唱八股发散千万不要散到自己不熟悉的区域&nbsp;]5.也讲讲为什么另一个项目选择令牌桶,具体流程6.&nbsp;OK,讲讲&nbsp;Redis&nbsp;的数据类型?还有吗?就了解这五种嘛[&nbsp;把5个的基础类型从应用对比到历届底层全都吟唱了一遍。一句还有吗直接没力气了,简历就写了理解5种,别的我是真一点没看TT&nbsp;]7.讲讲Redission分布式锁实现8.这个指数退避怎么实现的9.在这里有考虑去保障幂等性嘛10.这里为什么使用指数退避呢?&nbsp;什么时候用均匀重传[已经晕过去了说不了解,刚说了后就意识到,估计应该说指数退避能缓解压力防止下游服务器雪崩之类的]11.ok,那讲讲JMM12.讲讲RocketMQ如何保证的不丢消息13.讲讲RocketMQ延迟消息原理14.讲讲项目Redis实现会话记忆这一块15.如果ai调用function&nbsp;calling出现幻觉,有考虑怎么解决吗?[&nbsp;不了解,面试官说什么接口幂等化,高危操作人工防护,没在听,感觉人已经飞升了TT&nbsp;]16.mcp了解嘛?和function&nbsp;calling有什么区别[&nbsp;依旧不了解,只能说了个前者规范架构抽象解耦,后者耦合高只能算个工具调用]17.AI生成代码的代码质量怎么保障,那平时如何review的呢18.算法。lc215&nbsp;&nbsp;数组中最大第k个元素19.打算考研还是本科就业20.反问1️⃣有哪里不足,有哪些需要提高的部分。[主要说知识广度不够,多刷算法,让我别太紧张]2️⃣部门业务会做什么人生第二次面试。感觉大厂面试官的气场压力很大应该凉了不过这次面试非常锻炼心态,多面试,多面试。
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务