问题1:Explain需要关注的指标
需要重点关注type、rows、filtered、extra。
type由上至下,效率越来越高
ALL 全表扫描
index 索引全扫描
range 索引范围扫描,一般条件查询中出现了>、<、in、between等查询
ref 使用非唯一索引扫描或唯一索引前缀扫描,返回单条记录,常出现在关联查询中
eq_ref 类似ref,区别在于使用的是唯一索引,使用主键的关联查询
const/system 单条记录,系统会把匹配行中的其他列作为常数处理,如主键或唯一索引查询
null MySQL不访问任何表或索引,直接返回结果 虽然上至下,效率越来越高,但是根据cost模型,假设有两个索引idx1(a, b, c),idx2(a, c),SQL为"select * from t where a = 1 and b in (1, 2) order by c";如果走idx1,那么是type为range,如果走idx2,那么type是ref;当需要扫描的行数,使用idx2大约是idx1的5倍以上时,会用idx1,否则会用idx2
Extra
Using filesort:MySQL需要额外的一次传递,以找出如何按排序顺序检索行。通过根据联接类型浏览所有行并为所有匹配WHERE子句的行保存排序关键字和行的指针来完成排序。然后关键字被排序,并按排序顺序检索行。
Using temporary:使用了临时表保存中间结果,性能特别差,需要重点优化
Using index:表示相应的 select 操作中使用了覆盖索引(Coveing Index),避免访问了表的数据行,效率不错!如果同时出现 using where,意味着无法直接通过索引查找来查询到符合条件的数据。
Using index condition:MySQL5.6之后新增的ICP,using index condtion就是使用了ICP(索引下推),在存储引擎层进行数据过滤,而不是在服务层过滤,利用索引现有的数据减少回表的数据
explain各列属性的简介如下:
id:SELECT 的查询序列号,体现执行优先级,如果是子查询,id的序号会递增,id 值越大优先级越高,越先被执行;
select_type:表示查询的类型;
table:输出结果集的表,如设置了别名,也会显示;
partitions:匹配的分区;
type:对表的访问方式;
possible_keys:表示查询时,可能使用的索引;
key:表示实际使用的索引;
key_len:索引字段的长度;
ref:列与索引的比较;
rows:扫描出的行数(估算的行数);
filtered:按表条件过滤的行百分比;
Extra:执行情况的描述和说明。这一列显示一些额外信息,很重要。
慢SQL优化一般步骤?
1)通过慢查日志等定位那些执行效率较低的SQL语句
2)explain 分析SQL的执行计划
3)show profile 分析
了解SQL执行的线程的状态及消耗的时间。默认是关闭的,开启语句“set profiling = 1;”
4)trace
trace分析优化器如何选择执行计划,通过trace文件能够进一步了解为什么优惠券选择A执行计划而不选择B执行计划。
5)确定问题并采用相应的措施
5-1)优化索引
5-2)优化SQL语句:修改SQL、IN 查询分段、时间查询分段、基于上一次数据过滤
5-3)改用其他实现方式:ES、数仓等
5-4)数据碎片处理
问题2:RocketMQ如何实现高吞吐的
基于整体Broker的架构设计。对消息topic的区分,以及存储采用内存映射机制,如果在1.5G-2G范围,就可以很快从系统空间映射到用户空间,而磁盘文件中消息主体文件设计的最大为1G。通讯是采用netty,减少数据复制的磁盘IO和CPU资源。另外数据存储采用堆外内存做中间渠道,通过定时任务定时刷盘到磁盘文件中。
而磁盘文件分为三种类型、一种是全部消息、一种是消息队列文件、一种是索引文件。索引文件是key-value形式,key为消息队列中指定消息文件计算的hash值,value为偏移量。而消息队列文件中就是消息的逻辑信息和以及计算的hash值。以此就可以在主消息文件很快速的定位到消息。而主消息文件是顺序写入,是可以达到600MB/s。
以此来实现高吞吐。
问题3:mysql的分布式架构是什么样的?
\1. 数据分片
将整个数据库中的数据按照一定的规则分为多个分片,每个分片都存储一部分数据。分片的方式可以按照属性进行分片,比如按照用户ID或地理位置进行分片;也可以按照数据表或数据库进行分片,比如将不同表分别存储在不同的分片中。
\2. 代理层
代理层负责分发查询请求和负载均衡。当应用程序发送查询请求时,代理层会根据查询的相关信息,将请求分发到最合适的MySQL节点上。同时,代理层还会监控每个节点的负载情况,动态调整查询请求的分发策略,确保每个节点都能够得到均衡的负载。
\3. 存储节点
存储节点是实际存储MySQL数据的节点。每个存储节点都包含一个MySQL实例,负责存储一部分数据,并处理来自代理层的查询请求。为了确保数据的可靠性,通常会采用主从复制和多副本同步等机制,将数据备份到多个节点中。
MySQL分布式架构的实现方式
\1. Sharding-JDBC
\2. MySQL Cluster
\3. Vitess
问题4:发起一个http请求。到我们服务器。中间都是经过哪些流程
网络通讯7层协议
先dns解析。然后tcp连接。然后发送http请求。然后服务器处理
问题5:jvm是怎么工作的
1)Java程序服务启动加载过程
2)请求过来JVM是如何工作的
2-1)内存分配
2-2)线程相关
2-3)垃圾回收
问题6:查询InnoDB的一个普通索引。会回表的的执行过程
首先InnoDB有优化手段覆盖索引,普通索引属于二级索引,二级索引存放的主键ID。出于性能考虑,会优先把索引加载到内存中。所以如果是二级索引的话,先查询到对应的主键,再通过覆盖索引查询,若是没有查询到的数据,就需要回表查询(去聚簇索引中查询),查询到之后增量加载到内存中,并返回给服务器。
问题7:mysql中的日志文件。文件存的是什么。发送一条请求。数据是怎么在这些文件中流转的。
redo log:重做日志。会记录变更了数据页里的几个字节的值。表空间+数据页号+偏移量+修改几个字节的值+具体的值
undo log:撤销日志、undo 日志,它记录着事务回滚前的数据。undo 日志只记录事务中的增删改操作。
bin log : 二进制日志文件,这个文件记录了MySQL所有的DML操作。通过binlog日志我们可以做数据恢复,增量备份,主主复制和主从复制等等
当数据库修改页时,会将修改信息放入重做日志缓冲区,然后按一定频率(默认每秒一次)写入到重做日志文件。之后变更索引缓冲,对缓冲池中数据加锁。同时也会插入一条反向操作到回滚日志的缓存池中,通过刷盘机制进行存储到对应的日志文件中。
当事务提交的时候,会进行触发当前行数据的重做日志刷盘机制,直接刷盘到重做日志中,再通过后台线程更新到磁盘中。
若事务提交失败,则通过缓冲中的当前行数据里的回滚指针进行反向变更,并清除相关缓冲内容。
所以执行过程大概是 数据变更buffer,索引buffer,锁buffer,relog buffer, relog,磁盘。
问题8:mvcc是啥。undolog里存的是啥。
Mvcc 多版本并发控制。如果当前读取的数据正在更新/删除,这个时候不会去等待锁释放之后再去读取,而是去读取一个历史版本,这种读取也叫快照读。MVCC就是就是这种行为的实现。
MVCC依赖远行的三个隐藏字段,自增ID 事务ID 回滚指针、Read View、undo log。内部实现是根据事物ID和Read View来判断数据的可见性。如果事务ID在Read View的未提交列表中,则认为不可见。就根据回滚指针找到undo log中的历史版本做比较,即遍历链表中的事务ID,直到找到满足条件的DBTRXID,这个DBTRXID所在的旧记录就是当前事务能看到的最新老版本数据。
问题9:事务怎么隔离的。在可重复读和读提交的隔离级别下。mvcc生成的事务视图。有什么区别
事务是有4中隔离级别。读未提交、读已提交、可重复读、可串行读。
读未提交是最低的隔离级别,允许读未提交的数据。会发生脏读、幻读、不可重复读问题
读已提交和可重复读都是基于MVCC实现。但是读已提交的事务视图是数据变更之前生成的;而可重复读,是读取启动事务时生成的事务视图。
读已提交是读取的是缓冲中的数据,比如redo log缓存中的数据;而可重复读是读取redo log文件中的数据,都未刷盘到磁盘中,无法避免发生幻读现象。。
问题10:锁。synchronized和aqs。以及等待过程
常见锁是synchronized 和基于AQS数据结构实现的锁,用于特殊场景。
问题11:线程池内部工作原理、拒绝策略;线程池使用不当,会引发什么问题?
问题12:redis和数据库的数据。如果不一致怎么处理?
问题13:如果频繁fullGc。会考虑那些场景触发的
Full GC 的触发条件
- 调用 System.gc()
- 老年代空间不足
- 空间分配担保失败
- JDK 1.7 及以前的永久代空间不足
- Concurrent Mode Failure
命令行终端
- 标准终端类:jps、jinfo、jstat、jstack、jmap
- 功能整合类:jcmd、vjtools、arthas、greys
可视化界面
- 简易:JConsole、JVisualvm、HA、GCHisto、GCViewer
- 进阶:MAT、JProfiler
jps:显示指定系统内所有的虚拟机系统。-v显示启动JVM参数
Jstat : Jstat-gc 监控Java堆状态,容量情况 Jstat -gcutil监控Java堆各个区使用百分比
jinfo : jinfo -flag调整JVM参数 jinfo -pid查看配置信息
jmap:jmap -heap显示堆详细信息 jmap -dump生成对内存快照信息
jstack:jstack -pid打印堆栈
jstat:堆转存储快照分析工具
先看JVM启动参数,之后分析堆状态以及使用比,来确定是否是内存不够用;之后可以进行答应堆栈信息,来分析是不是线程相关的问题;也可通过答应内存快照进行分析,是否是手工GC还是老年代先关问题。
CMS常规分析
问题14:线程池内部怎么解决的内存泄漏问题?
由于ThreadLocalMap 的生命周期跟 Thread 一样长,对于重复利用的线程来说,如果没有手动删除(remove()方法)对应 key 就会导致entry(null,value)的对象越来越多,从而导致内存泄漏.但是。调用threadlocal的get/set方法。会判定如果key为null。则会把vlaue也设置为null。从而避免内存泄漏。为什么key会为null。因为key是弱引用。当gc出现就会把他回收
调用threadlocal的get/set方法。会判定如果key为null。则会把vlaue也设置为null。从而避免内存泄漏(内部优化)。
问题15: redis单线程还是单进程?
单线程
问题16: redis如果来了很多请求。都需要进行网络io处理和计算。怎么做到的单线程还很快呢?
Redis 是基于内存的,所以数据都是零拷贝,读写可采用IO多路复用
3, redis举个例子。比如订单缓存在数据库里。现在用户进来就点击订单功能。去查询不存在的订单。这种情况叫什么?
缓冲穿透
4, Redis单机如果已经处理不了那么多业务量了。怎么办呢。有哪些方案呢。
集群-主从从 哨兵模式
5, Redis的持久化方式是什么。
RDB AOF
6, 你了解哪些负载均衡的策略
轮训 随机 IP散列 加权轮训
7, Jvm的了解哪些。怎么怎么划分的区域?你了解过你之前项目中的一些参数吗。
公共: 堆 方法区 线程私有:程序计数器、虚拟机栈 本地方法栈
JVM参数 最大参数
8, Jmv的分代是新创建的对象是在年轻代的。那么有么有特殊情况呢。
看情况 大对象直接进老年代 小对象去年轻代的
9, 线程池你一般都是怎么用的?
使用构造器方式创建
10,线程之间有什么通讯方式?
显式或隐式
管道 、命名管道、消息队列、共享内存、信号量、套接字、信号
11, 除了一些方法的通讯还有什么可以让线程之间通讯呢?(他指的是内存可见性。通过内存屏障。这个问的很隐蔽)
显示传递 valiatle关键字 改变共享内存 隐式通知
12,什么情况下。会堆溢出。什么情况下会栈溢出。什么情况方法区溢出。
堆是创建对象的时候 内存不足 存活的对象过多
栈是线程 栈空间不足于用于分配线程执行过程中需要堆存分配
方法区是 主要是一些永久代的存放
13、你们是怎么跟前端交互的?
采用http协议进行通讯(套接字这种)
14、你对微服务这块了解多少。有实战经历吗
微服务也是分布式服务,只是服务划分上不一样。微服务是按照数据结构特性或者领域特性进行划分,以服务组件的形式存在。
15、mysql中。is null。和is not null。会走索引吗?
如果是普通索引。is null。不会。is not null。会;如果是主键索引。或者全文索引,就都会。
普通索引不会为null值创建条目。所以就会全表扫描。
1、spring事物,是怎么处理的数据
添加@Transactional注解就可以进行事务控制,Spring事务底层是基于数据库事务和AOP机制的。
实际执行步骤:
1.在一个方法上加了@Transactional注解后,Spring会基于这个类生成一个代理对象,会将这个代理对象作为bean。
2.当在使用这个代理对象的方法时,如果这个方法上存在@Transactional注解,那么代理则利⽤事务管理器创建⼀个数据库连接。
3.修改数据库连接的autocommit属性为false,禁⽌此连接的⾃动提交,这是实现Spring事务⾮常重 要的⼀步
4.然后执⾏当前⽅法,⽅法中会执⾏sql
5.执⾏完当前⽅法后,如果没有出现异常就直接提交事务,如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务
当然,针对哪些异常回滚事务是可以配置的,可以利用@Transactional注解中的rollbackFor属性进行配置,默认情况下会对RuntimeException和Error进行回滚。 而在数据处理过程中,事务的隔离级别默认是以Spring事务为准。那么其余数据管理环节可依据MVCC。
2、redis分布式锁。如果服务上锁了,上完锁以后服务挂了
Redis分布式锁实现方案:
1)SETNX + EXPIRE。即先用setnx来抢锁,如果抢到之后,再用expire给锁设置一个过期时间,防止锁忘记释放。
// 获取锁 基于 setnx 和 expire 此方法不会保证原子性 可以使用lua脚本(redis 又演变出 set加过期时间的方式)public boolean getLockNx(Jedis jedis, String lockeKey, String requestId, Long expireTime) {Long setnx = jedis.setnx(lockeKey, requestId);if (Objects.equals(setnx, 1)) {jedis.expire(lockeKey, new Long(expireTime).intValue());return true;}return false;
}
2)SET的扩展命令(SET EX PX NX)
// 获取锁, 设置超时时间,单位为毫秒 此方法目前可以满足大多数需求public boolean getLock(Jedis jedis, String lockKey, String requestId, Long expireTime) {String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);if (LOCK_SUCCESS.equals(result)) {return true;}return false;}
3)Redisson框架。只要线程一加锁成功,就会启动一个watch dog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。
4)删除锁。基于lua脚本释放 保证了原子性 和释放锁是符合自己的。
/*** 释放分布式锁 基于lua脚本释放 保证了原子性 和释放锁是符合自己的 解决并发问题* @param jedis Redis客户端@param lockKey 锁* @param requestId 请求标识@return 是否释放成功*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));if (LOCK_SUCCESS.equals(result)) {return true;}return false;
}
3、怎么获取运行环境
Java 可以通过 System 类提供的各种方法获取环境信息1. 获取操作系统的名称:
String os = System.getProperty("os.name");
System.out.println("操作系统名称:" + os);2. 获取操作系统的版本:
String osVersion = System.getProperty("os.version");
System.out.println("操作系统版本:" + osVersion);3. 获取Java运行时环境的版本:
String javaVersion = System.getProperty("java.version");
System.out.println("Java运行时环境版本:" + javaVersion);4. 获取Java虚拟机的版本:
String jvmVersion = System.getProperty("java.vm.version");
System.out.println("Java虚拟机版本:" + jvmVersion);5. 获取当前用户的用户名:
String username = System.getProperty("user.name");
System.out.println("当前用户名:" + username);6. 获取当前用户的主目录:
String homeDir = System.getProperty("user.home");
System.out.println("当前用户的主目录:" + homeDir);7. 获取当前工作目录:
String curDir = System.getProperty("user.dir");
System.out.println("当前工作目录:" + curDir);8. 获取Java类路径:
String classPath = System.getProperty("java.class.path");
System.out.println("Java类路径:" + classPath);9. 获取Java库路径:
String libPath = System.getProperty("java.library.path");
System.out.println("Java库路径:" + libPath);10. 获取临时文件夹路径:
String tmpDir = System.getProperty("java.io.tmpdir");
System.out.println("临时文件夹路径:" + tmpDir);
1、rpc和http的区别?
http请求是使用具有标准语义的通用的接口定向到资源的,这些语义能够被中间组件和提供服务的来源机器进行解释。
rpc的机制是根据语言的API来定义的,而不是根据基于网络的应用来定义的。
2、SpringSecurity简单说说 结合项目
SpringSecurity用于认证 他是基于Fiter链过滤最后注册到Spring容器中,交由Spring容器管理。另外它还有专属的上下文,用于存放认证信息。
3、Redis的删除策略是什么
定时删除、惰性删除、定期删除
**定时删除(时间换空间)。**定时删除时给key都设置一个过期的时间,当达到删除时间节点时,立即执行对key的删除。
**惰性删除。**当要删除的数据到达给定时间时,先不进行删除操作;等待下一次访问时,若数据已过期则进行删除,客户端返回不存在,数据未过期,则返回数据。
定期删除(折中)。
4、使用Redis怎么做延时消息
Redis实现延迟队列的是基于Redis事件机制 他有两个事件机制 一个是文件事件一个是时间事件。
文件事件是基于IO多路复用技术,监听多个套接字,并为套接字关联不同的事件处理函数。当套接字的可读或者可写事件触发时,就会调用相应的事件处理函数。
时间事件是分为定时事件、周期性事件。
而实现延时消息大概有几种:1、set类型设置,定时任务轮训;2、过期时间消息 回调处理;3、List设置专门的延迟消息,定时轮训 1、3 本质一样
5、Seata的Saga模式有了解么 底层是怎么实现的?
Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。
适用场景:**
1、业务流程长、业务流程多
2、参与者包含其它公司或遗留系统服务,无法提供 TCC 模式要求的三个接口**
6、如何设计延时任务?
ScheduledExecutorService
ScheduledExecutorService 接口可以实现延时任务。ScheduledExecutorService 可以安排一个任务在指定的时间开始运行,并且可以重复执行。相比 Timer,ScheduledExecutorService 更加灵活和可靠,因为它使用了线程池来管理任务执行。
CompletableFuture
Java 8 引入了 CompletableFuture 类,它可以实现异步执行和延时任务。CompletableFuture 可以使用 schedule() 方法来安排一个任务在指定的时间开始运行,也可以使用 delay() 方法来延时执行一个任务。
7、RocketMQ底层是怎么实现的。
RocketMQ 使用内存映射技术,数据持久化到磁盘中。可以参考MySQL的mysyma
8、分库分表是怎么实现的?
分库分表一般都是水平切分(水平分库、分表)、垂直切分(业务划分、冷热字段切分)。通常来说分库分表对业务代码都是无侵入式的,只需要关注业务逻辑SQL编码。
在分库场景中,通常会存在广播表。这种广播表在各个数据源中,数据完全一致。而广播表主要用于连表查询,避免跨库Join。通常这些表一般都是类似字典表,数据量少。
在分表中,最重要是的分片键。分片键决定了数据落地的位置,也就是数据将会被分配到哪个数据节点上存储。
当插入一条订单数据执行 SQL 时,需要通过解析 SQL 语句中指定的分片键来计算数据应该落在哪个分片中。以表中
order_no
字段为例,我们可以通过对其取模运算(比如order_no % 2
)来得到分片编号,然后根据分片编号分配数据到对应的数据库实例(比如DB_1
和DB_2
)。拆分表也是同理计算。ShardingSphere
还支持根据多个字段作为分片健进行分片。
**而分片策略一般都是分片算法和分片键组成。分片策略中可以使用多种分片算法和对多个分片键进行运算。**分库、分表的分片策略配置是相对独立的,可以各自使用不同的策略与算法,每种策略中可以是多个分片算法的组合,每个分片算法可以对多个分片健做逻辑判断。
常用的分片算法有很多:
- 哈希分片:根据分片键的哈希值来决定数据应该落到哪个节点上。例如,根据用户 ID 进行哈希分片,将属于同一个用户的数据分配到同一个节点上,便于后续的查询操作。
- 范围分片:分片键值按区间范围分配到不同的节点上。例如,根据订单创建时间或者地理位置来进行分片。
- 取模分片:将分片键值对分片数取模,将结果作为数据应该分配到的节点编号。例如, order_no % 2 将订单数据分到两个节点之一。
分库分表后在应用层面执行一条 SQL 语句时,通常需要经过以下六个步骤:SQL 解析
-> 执⾏器优化
-> SQL 路由
-> SQL 改写
-> SQL 执⾏
-> 结果归并
。
其中语法解析会将拆分后的 SQL 关键字转换为抽象语法树,通过对抽象语法树遍历,提炼出分片所需的上下文,上下文包含查询字段信息(
Field
)、表信息(Table
)、查询条件(Condition
)、排序信息(Order By
)、分组信息(Group By
)以及分页信息(Limit
)等,并标记出 SQL 中有可能需要改写的位置。 通过上边的 SQL 解析得到了分片上下文数据,在匹配用户配置的分片策略和算法,就可以运算生成路由路径,将 SQL 语句路由到相应的数据节点上。SQL 路由又根据有无分片健分为
分片路由
和广播路由
。 分片路由:直接路由、标准路由、笛卡尔积路由。
广播路由:全库路由、全库表路由、全实例路由、单播路由、阻断路由。
数据分片之后,一个逻辑表对应多个真实表,由此产生分布式ID。另外分库分表之后建议进行数据脱敏,以及通过分布式事务来管理多个数据源的原子性操作。
9、MySQL的原子性是通过什么来保证的?
mysql原子性利用了undo log redo log。
redolog恢复数据的完整性 undolog 做回滚
记录回滚的日志信息,利用undo log中的信息将数据回滚到修改之前。
mysql通过锁(lock)和事务技术对数据的原子性进行保障。
10、栈与队列是什么关系?
栈是先进后出的数据结构
队列是先进先出的数据结构
11、数组循环的几种方式?
for循环、while、do …while 、 for增强循环
吉利
1、填空题
1)Tread类本质上实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方式就收通过Tread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执run()方法。
2、选择题
1)对于JVM内存配置参数:
-Xmx10240m -Xms10240m -Xmn5120m -XXSurvivorRatio=3
,其最小内存值和Survivor区总大小分别是(D)
A 5120m,1024m
B 5120m,2048m
C 10240m,1024m
D 10240m,2048m
解析:
-Xmx:最大堆大小
-Xms:初始堆大小
-Xmn: 年轻代大小
-XXSurvivorRatio=3:代表Eden:Survivor = 3
根据Generation-Collection算法(目前大部分JVM采用的算法),一般根据对象的生存周期将堆内存分为若干不同的区域,一般情况将新生代分为Eden ,两块Survivor;
计算Survivor大小, Eden:Survivor = 3,总大小为5120,3x+x+x=5120 x=1024(Survivor区有两个,即将年轻代分为5份,每个Survivor区占一份),总大小为2048m。
2)ServletConfig继承什么类
Servlet
3)Math.ceil() “向上取整”, 即小数部分直接舍去,并向正数部分进1
Math.floor() “向下取整” ,即小数部分直接舍去
Math.round() “四舍五入”, 该函数返回的是一个四舍五入后的的整数
5、算法题
1)找出数组的最长共同前缀
思路(冒泡排序):
1-1)以数组的第一个作为对比依据
1-2)循环遍历剩下的数组
1-3)遍历的过程中,把第一个跟现在的数组比较,拿共同前缀出来。注意需要考虑数组的长度问题,避免数组下标越界问题。
1-4)拿上一个共同前缀跟下一个比较,得出最后一个共同前缀
1-5)返回共同前缀
2)任意输入数字,输出数组中排除输入数字之后数组的长度。
思路(冒泡排序):
2-1)设置初始数组长度为数组的长度
2-2)循环遍历数组
2-3)遍历过程中,若是与输入字符串一致就减一
2-4)返回最后的长度。
1、设计原则有哪些?SOLID
单一原则、开发封闭原则、里氏替换原则、接口隔离原则、依赖倒置原则、合成/聚合复用原则、迪米特法则
2、常用哪些设计模式?
建造者模式、外观模式、责任链模式、策略模式、简单工厂模式、适配器模式、装饰模式等
设计模式类型:创建型、结构型、行为型
3、当我们使⽤反射的时候,哪块代码体现了双亲委派机制呢?为啥SPI要破坏掉双亲委
派机制呢?
类加载环节
类加载器可见性原则。子类加载器能查看父类加载器加载的所有类,但是父类加载器不能查看子类加载器所加载的类。ServiceLoader和DriverManager类以及SPI接口类都是rt核心包的系统类,它们都是启动类加载器bootstrap classloader加载的。
而第三方jar包是在classPath下的,启动类加载器加载不了,也无法委派给父加载器加载,所以我们就需要破坏双亲委派机制,指定一个类加载器去加载。
4、泛型中的类型擦除和桥接⽅法指的是什么呢?怎么擦除的?如何桥接的呢?
类型擦除是编译的时候进行类型替换,替换成Object类型
桥接方法指的是为了解决由于类型擦除特性导致的方法不一致问题。因为擦除了之后,但是入参还是要符合规则。桥接方法
是编译器自动生成的方法,无需人工专门处理。
5、多线程协调操作方法:
中断 interrupt() 、 isInterrupt() 、 interrupted() 、
睡眠 sleep() 、 wait() 、恢复 notify() 、
挂起 suspend() 、继续执行 resume() 、
暂停 调用 别的资源 join() 、 yield()
6、Synchronized锁的加锁流程和优化策略( 偏向锁加锁 、 轻量级锁 、 ⾃旋锁 、 重
量级锁 、 锁消除 )
无锁-》轻量级锁 -》重量级锁
无锁 -》偏向锁 -》重量级锁
Synchronized加锁是以锁对象来进行加锁的,线程在获取锁之前,会把对象头中Mark Word拷贝到锁记录中。Mark Word中有偏向锁的线程ID,锁标志,若是线程ID是当前线程ID,且锁状态为无锁标志,竞争到锁之后则是偏向锁。若不是当前线程ID,且锁状态为无锁标志,竞争到锁之后则是轻量级锁。若是已经拿到锁之后,还有别的锁竞争就会升级为重量级锁。其他没有拿到锁的线程就会进行自旋。
若是加锁的范围内,还有对内部别的方法进行加锁,就会进行内部优化,执行锁粗化,进行线程内部的锁消除。
7、多线程⼯具包JUC类:Condition 、 Semaphore 、 ReadWriteLock 、 CountDownLatch 、 CyclicBarrier 、 LockSupport
Condition 监视器方法
ReadWriteLock 读写锁
Semaphore 计数信号量
CountDownLatch 倒数门栓
CyclicBarrier 循环栅栏
LockSupport 阻塞线程
8、以下在多线程中是做什么用的?其内部是怎么实现的?
ConcurrentHashMap 在多线程中用于使用HashMap保证线程安全,其内部使用ReentrantLock进行加锁,ConcurrentHashMap最大长度为16,是针对segment来进行加锁的。
另外它的put方法是使用的synchronized。
ThreadPoolExecutor用于创建线程池,其内部有核心参数用于调配CPU的资源
ThreadLocal 用于线程池的当前线程实例变量,在使用的时候会进行创建。
AbstractQueuedSynchronizer 用来构建锁或者其他同步组件的基础框架。其内部是有个共享变量以及同步队列、条件队列。
9、类加载流程
加载、验证、准备、解析、初始化
10、符号引⽤和直接引⽤的区别?
直接引⽤是直接指向目标的指针
符号引用是一个字符串,它给出了被引用的内容的名字并且可能会包含一些其他关于这个被引用项的信息——这些信息必须足以唯一的识别一个类、字段、方法。
11、值传递与引用传递的区别?
值传递就是复制一份参数传递到函数内部使用。不对实际值做任何改变。
引用传递是参数给堆内存上的引用地址,若是对形参进行改变,就会影响实参。
12、JVM⾯试主⻆JMM及栈帧实操及详解
JMM内存区域
线程共享:堆、方法区
线程私有:程序计数器、虚拟机栈、本地方法栈
线程启动会进行对线程需要使用的变量以及对象信息进行入栈出栈。
13、Java中的对象引用
强引用 new就是强引用
软引用
弱引用
虚引用 只是会有个类似通知
14、什么是 逃逸分析 ?
**是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。**通过逃逸分析出一个新的对象的引用的使用范围,从而决定是否要将这个对象分配到堆上
15、什么是 标量替换 ?
指向对象的引用也是标量;而对象本身则是聚合量(aggregate),可以包含任意个数的标量。如果把一个Java对象拆散,将其成员变量恢复为分散的变量,这就叫做标量替换。
16、什么是 TLAB ?
本地线程缓存
17、什么是 槽位复⽤ ?
局部变量表中的槽位。
18、垃圾回收算法
引⽤计数法 、 标记清除法 、 复制算法 、 标记压缩算法 、 分代算法 、 分区算法
19、平时使⽤哪种垃圾回收器?
JDK默认的 多线程高吞吐年轻代+老年代垃圾回收期
20、JVM调优和排查问题?
有问题表现就以问题表现来排查问题,若是没有的话,则可以通过多个方面分析
1、内存使用率
2、CPU使用率
3、年轻代空间
4、是否频繁GC
5、GC快照等。
Kafka与RocketMQ的区别
Kafka 和 RocketMQ 在消息队列领域都是比较流行的开源软件,但是使用情况是因应用场景而有所不同。
**Kafka 更适用于大数据领域,例如数据传输、流式处理、日志聚合等高吞吐量的场景。**因为 Kafka 的设计注重性能和可靠性,具有高吞吐量、低延迟、数据持久性、可扩展等特点,也广泛应用于日志收集、监控、大数据分析等领域。
**RocketMQ 更适合于消息传输和业务解耦场景,例如电商平台的订单系统,支付系统和物流系统之间的消息通信。**RocketMQ 的特点是可靠性强,支持事务消息,性能好,能够快速处理消息,具有高可用、易扩展等优点。RocketMQ 还能够实现消息轨迹追踪,方便进行分布式系统中的故障排查。
因此,选择 Kafka 或 RocketMQ 要根据具体场景和业务需求来决定。在大数据处理方面,Kafka 的使用较为广泛;而在消息传输方面,RocketMQ 也是一个不错的选择。
kafka丢消息,配置参数不丢以后性能下降的厉害,所以适合日志存储。