百度Java日常实习一面

最近每天更新一篇,都是老素材去年面的。想冲一冲1000人品的奖章。uu们觉得有帮助的话不妨点个追番,送送花,陈某定带你一路飞驰(凡人修仙传台词)。

1.自我介绍

2.项目里有什么技术难点吗?

3.kafka的架构

4.kafka怎么保证数据不丢失

5.kafka怎么解决重复消费问题

6.kafka的消费顺序性怎么保证

7.AQS原理(简历里写了就特别喜欢问)

8. jvm内存划分,垃圾回收

9.JAVA对象的生命周期

10.你用的jdk版本,是哪个垃圾回收器。还了解其它垃圾回收器吗

11.redis数据结构,说几条redis原生命令

12.mysql三大日志,存了什么,作用

13.Spring里怎么实现事务

14.让你自己实现一个@ Transaction注解你怎么写

15.代码题:多线程累加

16.续15.除了用syschronized你还能怎么实现

3.

kafka基于发布订阅模型。一个分区可以看作一个队列,生产者发送消息时指定topic,每个topic内部又有多个partition,这个partition分区就可以看作一个队列。一个Broker是一个独立的kafka实例。而消费者一般都在消费者组里,每个消费者组可以选择订阅一个或多个topic,kafka会按topic将里面的分区平均分配给消费者组里的消费者们。

例如Topic A里有三个分区p1,p2,p3。消费者组1订阅了A,且有两个消费者线程a,b。那么就可能p1,p2分配给a,p3分配给b。

要注意一个分区只能由消费者组里的一个线程来消费,不可能a,b同时都消费某个分区,这是为了保证消费的顺序性。

例如topicA里有两个分区p1,p2。消费者组1里有三个消费者线程a,b,c。这样会有一个消费者空闲,不会出现a,b同时去消费p1的情况。

一个topic可以被多个消费者组订阅,每个消费者组维护自己的offset。

一个消费者组也能订阅多个topic,这时会按分区数量去平均分配.

4.数据持久化机制,拿到数据会持久化到磁盘;

写前日志WAL;

生产者:可能数据调用send后由于网络问题并没有真正发送成功,可以通过把send改为同步操作或者用回调函数;

消费者:消费者可能还没有真正消费就提交了offset,改为消费完了才提交,而不是一拿到就提交;

kafka本身:有个acks参数,设置成all表示所有副本都同步了才响应,安全性最高

5.重复消费的根本原因是因为消费者消费了,但提交offset出了问题。

最好是把提交消息改成幂等操作。例如生产者发送消息时,将唯一的订单id写到消息里,消费者拿到去redis或数据库看该id的订单存不存在,存在就说明已经消费过(可以做成mysql唯一键,这样再写入会失败)。

也可以消费者一拿到消息就提交offset,但这样会出现数据丢失的问题(提交完后有了问题,导致消息没有真正被消费),需要做数据兜底。

6.一个分区partition里的消息是能够保证消费的顺序性的,因为一个分区只有一个线程来消费,所以就要想办法将需要保证顺序性的这些消息都发送到同一个分区,通过给这些消息指定相同的Key来完成,因为kafka默认是通过hash(key)来进行分区,key相同就分到一个区了。producer.send(new ProducerRecord<>("topic", key, value));

7.AbstractQueuedSynchronizer是抽象队列同步器,是java并发包中提供的构建锁和同步器的基本框架。

首先是它有一个volatile修饰的int型变量state,在ReentrantLock里它表示锁被获取的次数,在Semaphore里它表示剩余的许可数量。一般我们通过cas操作来修改这个变量,cas成功表示获取锁成功,否则失败。

同时,aqs内部维护了一个同步队列,用来存储那些等待获取锁的线程。当线程获取锁失败时,就会把线程引用包装成一个结点放到队列里去。当持有锁的线程释放资源时,会调用release方法,在释放锁之后会把队列里的第一个结点唤醒,被唤醒的线程会去尝试获取锁。

aqs是基于模板方法设计的,它定义了一系列模板方法,如acquire,acquireShared,release,releaseShared等等,我们可以写一个aqs的子类,然后自己实现tryAcquired,tryReleased等几个抽象方法。然后就能实现自定义的同步器。

8.每次都有这个,这次就不写在这了

9.加载:通过类的全限定名找到.class文件,并读到内存,在方法区里生成Class对象

验证:检查class文件是否符合规范

准备:为类的静态变量分配内存,并初始化为默认值。 如static int count = 1;

解析:将常量池里的符号引用转换为直接引用。

初始化:执行类的<clint>方法。

卸载:将类从JVM中移除。

说到类的卸载,不妨再延伸一下。

怎样的类能被卸载:a.该类的所有实例都不可达;b.类的加载器不再被引用;c.对应的Class对象也不可达

一般类被加载后都会长期驻留,典型的会卸载的场景是热部署场景。

且类的卸载都是fullGC干的,只有fullGC会回收方法区。

10.jdk1.8对应的垃圾回收器是ParrallelGC,在新生代和老年代分别是Parallel Scavenge和Parrallel Old。

jdk9之后默认G1,常见的还有CMS,在jdk14之后就淘汰了。

让gpt对比了一下几种常见垃圾回收器,具体说的话太复杂,一般可以稍微记一段话,好应付面试,其实问这个的也很少。

CMS垃圾回收过程

1:初始标记:从GC Roots找能到达的老年代对象,有STW

2:并发标记:从1阶段标记的对象出发找能不能到其它老对象,并发无STW

3:重新标记:STW,修改并发标记期间产生的变化

4:并发清除

G1:主要是将堆区域划分为了Region,每个region都能根据需要扮演Eden,Survivor或者老年代。

G1最大的特点是它的“停顿时间模型”用户可以指定期待停顿时间(默认为200ms)。

G1会记录每个Region的回收耗时、标准偏差、置信度等统计信息,然后通过这些信息预测现在开始回收的话,有哪些Redion组成回收集才可以在不超过预期停顿时间的约束下获得最高的收益。

——————————————————————————————————————————

这里想到一个比较有趣的问题,Eden,Survivor,老年代在堆中的占比是多少?以及这些垃圾回收器适合用在新生代还是老年代?我们常说的三种垃圾回收算法,标记清除,标记复制,标记整理分别适合用在哪个区域?(也是以前小米一面被问得,也就被问过一次,挺难的)

新生代占堆的1/3,其中Eden可能占新生代的80%。新生代里大部分对象都是朝生夕灭,Eden肯定占大部分,因为每次都是Eden和survivor中一个分区的存活对象复制到另一个区域,能存活下来的比例是比较小的,survivor不适合太大。

至于为什么堆占2/3,这种一般是实验出来的。只能说大致理解一下为什么老年代更大,因为老年代的对象生命周期长,GC频率低,回收成本高,空间小了会导致频繁MajorGC和FullGC,且大对象都是直接进入老年代,老年代需要大空间。

说到Eden,s0,s1其实就是复制标记算法,分为两半,每次活的复制过去嘛。老年代一般适合标记清除和标记整理,因为变动少,用标记复制会浪费空间。

至于各种垃圾回收器适合新生代还是老年代,一般明确说的只有CMS适合老年代,顾名思义,concurrent Mark Sweep,并发标记清除,正如上述,标记清除适合老年代。

11.redis数据结构大家肯定都知道,String Hash Set ZSet List

原生命令用的少确实不记得,用简单的就是set count 1

12.binlog:主从复制,记录的是数据操作日志,是逻辑日志,如将id=10的age改为5。

redolog:持久性,记录的是物理操作日志,那哪个具体位置改了什么.

undolog:原子性,旧版本的值

13.一般都是用的@Transaction注解;

14.

本质是用AOP

首先自定义一个注解,编写AOP切面类,拦截带注解的方法。,在方法执行前开启数据库事务,方法正常执行则提交,发生异常则回滚,确保方法内所有数据库操作作为一个整体执行。

全部评论
点赞 回复 分享
发布于 今天 01:20 江苏

相关推荐

mjasjon:这种trash中厂 简历过筛概率比大厂还低(除阿里系)
投递哔哩哔哩等公司6个岗位
点赞 评论 收藏
分享
评论
1
3
分享

创作者周榜

更多
牛客网
牛客企业服务