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

全部评论

相关推荐

昨天 23:30
已编辑
小红书_后端开发
请先做一个简单的自我介绍。对于Java中的锁机制,你有什么理解?在悲观锁中,Java语言层面有哪些实现方式?它们之间的区别是什么?synchronized和reentrant&nbsp;lock在等待与唤醒机制上有什么区别?你对线程池的理解是怎样的?在使用线程池执行任务时,一般需要注意哪些问题?如何让主线程感知到线程内部的异常?如果线上应用频繁出现GC问题,可能是什么原因导致的?Spring&nbsp;AOP使用的哪种设计模式及代理方式?在哪些场景下会使用Spring&nbsp;AOP,以及使用时应注意哪些问题?问:InnoDB数据库中的索引使用何种数据结构,B+树和B树有何区别?在MySQL中,如何通过explain查询来分析circle执行计划并找出性能差的原因?当查询涉及到多个字段且索引设计有问题时,该如何排查和优化?对于存储数据量大的表,应如何分析其性能问题并提出解决方案?在处理频繁的修改和查询操作时,如何避免引发性能问题?问实习:在对象存储中,你们采用了哪两种经典方式?当时在测试环境中遇到了什么新问题?为了解决这个问题,你们采取了什么优化措施?....算法题:好像easy还是middle直接秒了(已经好久好久没刷题了&nbsp;稍微写慢了一点)反问:部门业务大概是什么样的?答:部门属于公司个性化工程平台部,主要负责个性化让利、触达、超级VIP体系以及用户画像和标签数据四块业务。
查看17道真题和解析
点赞 评论 收藏
分享
03-12 19:44
已编辑
华南农业大学 Java
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

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