中通 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圣经

全部评论

相关推荐

03-16 18:50
已编辑
广东工业大学 Java
🖊笔试部分1.&nbsp;分别给出重载和重写的例子,然后说明他们的区别2.&nbsp;异常为什么要传递异常信息,怎么捕获3.&nbsp;字符串a,b,c...变成a-&gt;b-&gt;c4.&nbsp;List&lt;Integer&gt;怎么去重5.&nbsp;状态机状态流转思路(随便写了if&nbsp;else)6.&nbsp;Post请求参数的方式有哪些7.&nbsp;Autowired多bean注入问题的解决方案(至少两个)8.&nbsp;多线程并行处理任务:同时处理4个任务,每个任务下面还有3个子任务,如何高效处理(没写出来)📕面试部分1.&nbsp;先自我介绍2.&nbsp;写一个Switch,用String类型做判断,一到两个分支就好(写出来了,但是新旧版本写混了,把&nbsp;':'&nbsp;和‘-&gt;’也给同时写出来了😂;还有我还写了&nbsp;非空判断&nbsp;的句子,他说java17后不用这么写了?)3.&nbsp;Mysql查询user表和phone表,查询出手机号大于2个的用户&nbsp;(写的也不怎么好,还问我为什么要用join,有什么优势)4.&nbsp;问了我笔试的第8道题,因为我没写出来5.&nbsp;然后还问了我线程池提交任务后返回值是什么6.&nbsp;提问环节😃总结这是我的第一次面试,我个人感觉应该是挂了,面试问我这几个问题我都答不出来,连项目都没问我😂。整个面试流程得一个小时左右,进去做完笔试后还等了一会儿才去面试。面试官人还挺好的,程序员格子衫形象,还亲自指出我现在的问题,受益匪浅,我真的很感动。
查看5道真题和解析
点赞 评论 收藏
分享
评论
点赞
2
分享

创作者周榜

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