一面
数据结构
- 链表有哪些分类?
单向或双向、是否带头节点、是否循环 - 树有哪些?
平衡二叉树、二叉查找树、红黑树 - 跳表结构
Java基础
- final关键字的用途?
修饰位置 | 作用 | 备注 |
---|---|---|
类 | 不可被继承 | |
方法 | 不可被子类重写 | |
变量 | 修饰只读变量,如果修饰对象,对象引用不能修改,对象值可修改 |
遗留问题:Integer对象
Integer intObj = 129;
final Integer intObjF = intObj;
intObj = 130;
System.out.println(intObjF);
-
final修饰类可以被spring管理吗?
参考:不可以。
Spring管理的类依赖动态代理实现。
动态代理有两种实现,JDK和CGLIB,JDK动态代理只能代理实现了接口的类,CGLIB动态代理是通过生成被代理类的子类,来拦截被代理对象的方法调用。 -
final修饰类,可以对这个类做AOP切面编程吗?
-
synchronized关键字,在对象头的什么位置?
-
ArrayList扩容实现
-
HashMap如何扩容的?
-
String+String相比StringBuilder的坏处体现在哪里?
如果是显示的在代码里使用了+拼接字符串,JDK会优化成StringBuilder,但如果在for循环里使用+号拼接,JDK的优化是每次生成一个StringBuilder对象,每次开辟新的内存空间浪费内存。
StringBuilder底层实现是字节数组,无线程安全设计。 -
线程安全的集合,实现原理?
设计模式
- 常用的设计模式都有哪些?
- 结合Spring框架,说一些设计模式
Java框架
- Bean的生命周期?
Bean的完整生命周期经历了各种方法调用,这些方法可以划分为以下几类:
1、Bean自身的方法:这个包括了Bean本身调用的方法和通过配置文件中的init-method和destroy-method指定的方法。
2、Bean级生命周期接口方法:这个包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法。
3、容器级生命周期接口方法:这个包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”。
4、工厂后处理器接口方法:这个包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工厂后处理器 接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。(对应上图中的BeanFactoryPostProcessor)
参考:Spring常见面试题总结,Spring Bean的生命周期(非常详细)
- Spring AOP实现原理?
- SpringBoot默认MySQL连接池?
答案:HikariDataSource - MyBatis实现原理?
动态代理
MySQL
-
MySQL有哪几种日志文件?(binlog、undolog、redolog)
1)binlog:记录了包含DDL(表结构变更)和DML(数据变更)的操作。主要有3种格式,Statement(记录SQL语句)、ROW(每一行具体的变更)、Mixed(混合前面两者)
2)undolog撤销日志:记录逻辑日志的SQL,在事物需要回滚时用到,保证原子性,比如事物执行了一条DELETE,则undolog记录一条INSERT。
3)redolog重做日志:InnoDB引擎特有,记录页的修改,事物提交时将redolog按照刷盘策略刷到磁盘,(InnoDB使用页为单位管理存储空间,将磁盘上的页缓存到Buffer Pool提高性能),大小固定,采用循环写。 -
MySQL Explain执行结果中,type结果的枚举。
参考:对应问题MySQL执行计划。
Explain执行结果中几个重要的参数:
列名 | 含义 | 解释 |
---|---|---|
type | 表的访问方法 | system:如果表使用的引擎对于表行数统计是精确的(如:MyISAM),且表中只有一行记录的情况下,访问方法是 system ,是 const 的一种特例。const:表中最多只有一行匹配的记录,一次查询就可以找到,常用于使用主键或唯一索引的所有字段作为查询条件。 eq_ref:当连表查询时,前一张表的行在当前这张表中只有一行与之对应。是除了 system 与 const 之外最好的 join 方式,常用于使用主键或唯一索引的所有字段作为连表条件。 ref:使用普通索引作为查询条件,查询结果可能找到多个符合条件的行。 index_merge:当查询条件使用了多个索引时,表示开启了 Index Merge 优化,此时执行计划中的 key 列列出了使用到的索引。 range:对索引列进行范围查询,执行计划中的 key 列表示哪个索引被使用了。 index:查询遍历了整棵索引树,与 ALL 类似,只不过扫描的是索引,而索引一般在内存中,速度更快。 ALL:全表扫描。 |
key | ||
Extra |
- MySQL事务隔离级别?
- MySQL索引失效的场景?
- MySQL分库分表怎么做的?
- MySQL in()查询条件索引失效的场景?
- MySQL 分库分表是否做过?
Redis
- Redis分布式锁实现方式
- setnx命令
# 设置某个 key 的值并设置多少毫秒或秒 过期。
set <key> <value> PX <多少毫秒> NX
或
set <key> <value> EX <多少秒> NX
setnx对应到Java里的实现为setIfAbsent:
// 1.生成唯一 id
String uuid = UUID.randomUUID().toString();
// 2. 抢占锁
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 10, TimeUnit.SECONDS);
if(lock) {
System.out.println("抢占成功:" + uuid);
// 3.抢占成功,执行业务
List<TypeEntity> typeEntityListFromDb = getDataFromDB();
// 4.获取当前锁的值
String lockValue = redisTemplate.opsForValue().get("lock");
// 5.如果锁的值和设置的值相等,则清理自己的锁
if(uuid.equals(lockValue)) {
System.out.println("清理锁:" + lockValue);
redisTemplate.delete("lock");
}
return typeEntityListFromDb;
} else {
System.out.println("抢占失败,等待锁释放");
// 4.休眠一段时间
sleep(100);
// 5.抢占失败,等待锁释放
return getTypeEntityListByRedisDistributedLock();
}
以上代码中,获取当前锁的值和清理锁两步非原子性,在并发场景下可能释放其他线程的锁(比如任务执行时间超过锁过期时间,此时另一个任务可以获取到锁)。
- Redisson
redis官方对使用Redis实现分布式锁提供了一个通用算法Redlock,Java版的分布式锁实现就是Redisson
Spring Boot 整合 Redisson
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.15.5</version>
</dependency>
@Configuration
public class MyRedissonConfig {
/**
* 对 Redisson 的使用都是通过 RedissonClient 对象
* @return
* @throws IOException
*/
@Bean(destroyMethod="shutdown") // 服务停止后调用 shutdown 方法。
public RedissonClient redisson() throws IOException {
// 1.创建配置
Config config = new Config();
// 集群模式
// config.useClusterServers().addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");
// 2.根据 Config 创建出 RedissonClient 示例。
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
分布式可重入锁
RLock lock = redisson.getLock("anyLock");
// 最常见的使用方法
lock.lock();
(1)多个线程抢占锁,后面锁需要等待,可重入锁是阻塞的。
(2)如果抢占到锁的线程所在的服务停了,锁会被释放。Redisson内部提供了一个监控锁的”看门狗“,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。如果我们未指定 lock 的超时时间,就使用 30 秒作为看门狗的默认时间。只要占锁成功,就会启动一个定时任务:每隔 10 秒重新给锁设置过期的时间,过期时间为 30 秒。如果设置了锁的自动过期时间,则执行业务的时间一定要小于锁的自动过期时间,否则就会报错。
分布式读写锁
RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常见的使用方法
rwlock.readLock().lock();
// 或
rwlock.writeLock().lock();
Redis 分布式锁|从青铜到钻石的五种演进方案
分布式锁中的王者方案 - Redisson
- 持久化的方式?
- Redis数据结构?
MQ
- RabbitMQ和Kafka的区别?
参考:
1)吞吐量的差别:Kafka吞吐量每秒几十万,RabbitMQ每秒几万。
2)RabbitMQ对每个消费者创建一个队列。
3)消息顺序:RabbitMQ通过多线程消费,失败会重新入队,有顺序问题。Kafka每个分区数据是有序的。 - 消息积压如何处理,需要注意什么?
参考:首先定位是生产积压还是消费积压,可以通过控制台快速定位,对于消费积压,排除消费失败重复消费的情况,可以考虑水平扩容、关闭一些不重要的业务。
二面
多线程
- 重排序有哪些分类?如何避免?
- Java 中新的Lock接口相对于同步代码块(synchronized block)有什么优势?如果让你实现一个高性能缓存,支持并发读取和单一写入,你如何保证数据完整性。
- 如何在Java中实现一个阻塞队列。
- 写一段死锁代码。说说你在Java中如何解决死锁。
- volatile变量和atomic变量有什么不同?
- 实现Runnable接口和Callable接口的区别
- 执行execute()方法和submit()方法的区别是什么呢?
- AQS的实现原理是什么?
- java API中哪些类中使用了AQS?
线程池
- 线程池参数如何设置?
参考:Java并发 - 线程编排框架解决了什么问题?
参考:1) 通过压测,找到最好的参数配置。 2)简化多线程开发。
分布式
- 分布式
CAP 理论 和 BASE 理论、Paxos 算法和 Raft 算法 - RPC框架用的什么,OpenFeign是怎么做的?
分布式事务
分布式 ID - 高并发
消息队列
读写分离&分库分表
负载均衡 - 高可用
限流
降级
熔断
JVM
中间件
- SkyWalking实现原理
ES
- 写ES时有没有遇到数据版本问题的报错
- ES怎么部署的,如何实现高可靠,对ES有没有降级?
Kafka
- Kafka如何实现零拷贝,为什么快?
参考: Kafka使用了零拷贝,具体是通过sendFile实现零拷贝。
零拷贝相比传统数据传输,减少了数据拷贝次数、减少了上下文切换。
DMA,英文全称是 Direct Memory Access,即直接内存访问。DMA本质上是一块主板上独立的芯片,允许外设设备和内存存储器之间直接进行 IO 数据传输,其过程不需要 CPU 的参与。
一般数据拷贝过程:
整个过程发生了4次用户态和内核态的上下文切换和4次拷贝,具体流程如下:
用户进程通过read()方法向操作系统发起调用,此时上下文从用户态转向内核态
DMA控制器把数据从硬盘中拷贝到读缓冲区
CPU把读缓冲区数据拷贝到应用缓冲区,上下文从内核态转为用户态,read()返回
用户进程通过write()方法发起调用,上下文从用户态转为内核态
CPU将应用缓冲区中数据拷贝到socket缓冲区
DMA控制器把数据从socket缓冲区拷贝到网卡,上下文从内核态切换回用户态,write()返回
mmap零拷贝原理:
sendFile零拷贝原理
Java实现零拷贝的API
public static void main(String[] args) {
try {
FileChannel readChannel = FileChannel.open(Paths.get("./a.txt"), StandardOpenOption.READ);
FileChannel writeChannel = FileChannel.open(Paths.get("./b.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//用于mmap做内存映射
MappedByteBuffer data = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, 1024 * 1024 * 40);
//数据传输
writeChannel.write(data);
/* //sendfile方式零拷贝
long len = readChannel.size();
long position = readChannel.position();
//数据传输
readChannel.transferTo(position, len, writeChannel);*/
readChannel.close();
writeChannel.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
参考与感谢:阿里二面:什么是mmap?
- Kafka架构怎么设计的?
- Kafka相比RabbitMQ的区别
Flink
- Flink任务执行过程
系统设计
-
服务迁移应该考虑什么?
参考:域名复用、切量。 -
项目中有没有哪些指标,是通过技术手段优化得到提升的?
-
QPS很高,没有使用本地缓存的原因?
-
简历里XXX项目瓶颈在哪里?