面试常见问题一二

ArrayList和linkedList区别:

底层数据结构不同,数组和链表.
ArrayList需要考虑扩容,一般用于随机查找,尾部添加数据比较快.
linkedList  增删改

MySQL 存储引擎

InnoDB存储引擎:
特点:

InnoDB 支持事务操作;(每一条SQL都默认封装成事务,自动提交,会影响速度)
InnoDB 支持外键;
InnoDB 是聚集索引(聚簇索引);
InnoDB 不保存表的总条数;
InnoDB 5.7版本之前不支持全文检索;
InnoDB 支持表级锁、行级锁,默认为行级锁;
InnoDB 表必须有主键(如果我们没有明确去指定创建主键索引。它会帮我们隐藏的生成一个 6 byteint 型的索引作为主键索引);
InnoDB 文件存储方式为.frm文件存储表结构,ibd文件存储数据内容。

MySQL除了主键索引外,都是非聚簇索引。即只有我们创建的主键id索引,我们可以叫他id索引,它别名又叫做聚簇索引,理解成一个别名的即可。如果我们再创建一个name索引,它就叫做非聚簇索引。

MyISAM存储引擎:
特点:
MyISAM 是非聚集索引;
MyISAM 有一个变量专门来保存整个表的行数,查询count很快(注意不能加任何 where 条件)
MyISAM 支持全文索引;
MyISAM 可以被压缩后进行查询操作,节省空间容量;
MyISAM 支持表级锁,不支持行级锁;
MyISAM 中主键不是必须的;
MyISAM 文件存储方式为.frm文件存储表结构,.MYD文件存储数据内容,.MYI文件存储索引文件。
区别:1.InnoDB 支持事务,MyISAM 不支持。对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;

  2. InnoDB 支持外键,而 MyISAM 不支持。对一个包含外键的InnoDB表转为MYISAM会失败; (外键现在用的也不多,因为它关联性太强,如果要删除一个表,会因为有外键的关联而导致删除失败。通常是通过 table a = table b on a.id = b.id 这种两表关联的方式来间接替代外键作用 )

  3.InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的;MyISAM是非聚集索引,它也是使用B+Tree作为索引结构,但是索引和数据文件是分离的,索引保存的是数据文件的指针。
  4.InnoDB 必须要有主键,MyISAM可以没有主键;InnoDB 如果我们没有明确去指定创建主键索引。它会帮我们隐藏的生成一个 6 byteint 型的索引作为主键索引。

  5.InnoDB辅助索引和主键索引之间存在层级关系;MyISAM辅助索引和主键索引则是平级关系。即:InnoDB 如果添加其他辅助索引,辅助索引查询就需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也相应都会很大。(比如表中有id(主键),name,age 字段,我们创建一个主键索引,再来创建一个name索引,层级关系如下图所示)
 6. InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快(注意不能加有任何WHERE条件);

  7. Innodb不支持全文索引,而MyISAM支持全文索引,在全文索引领域的查询效率上MyISAM速度更快高;(MySQL 5.7 版本以后,InnoDB也支持全文索引了)

  8. InnoDB支持表级锁、行级锁,默认为行级锁;而 MyISAM 仅支持表级锁。InnoDB 的行锁是实现在索引上的,而不是锁在物理行上。如果访问未命中索引,也是无法使用行锁,将会退化为表锁

  9. Innodb存储文件有frm、ibd,而Myisam是frm、MYD、MYI。【InnoDB 中,.frm文件:保存的是表结构定义描述文件;.ibd文件:保存的是employee表中的数据内容】;【MyISAM中,.frm文件:保存的是表结构定义描述文件,.MYD文件:保存的是数据内容,.MYI文件:保存的是索引内容】(好像是在MySQL 8.0中,.frm 文件已经不存在了,此处以MySQL5.7介绍)
如何选择?
判断是否需要支持事务,如果要请选择 InnoDB,如果不需要可以考虑MyISAM;

如果表中绝大多数都只是读查询,可以考虑 MyISAM,如果既有读也有写,那还是使用InnoDB吧。

系统奔溃后,MyISAM恢复起来更困难;

MySQL5.5 版本开始 InnoDB 已经成为 MysQL 的默认引擎(之前是MyISAM),说明其优势是有目共睹的,如果你不知道用什么,那就用InnoDB,至少不会差。

sql调优:

![在这里插入图片描述](https://img-blog.csdnimg.cn/99f01fb576284d729df06f58d86f4bd4.png)**查看sql执行计划语法**: explain select 语句;
	1  id: select 查询的序列号,标识执行的顺序;
	2  select_type 查询的类型,主要是用于区分普通查询,联合查询,子查询等..DEPENDENT SUBQUERY:重要:出现这个,说明性能很差
	3  table: 	查询涉及到的表,直接显示表名或者表的别名
	4  partitions  :有分区的时候显示
	***5***  type   	访问类型(重点)。SQL查询优化中一个很重要的指标,结果值从好到怀依次是:system> const> eq_ref> ref> range> index> ALL。(SQL优化至少要达到 range 级别为好,越高越好)
	***6***   key : 	查询过程中,实际使用到的索引,如果为 Null,则没有使用索引
		key为 PRIMARY,表示使用了主键,rows代表查询了行数
	7  filtered:  filtered的值越大越好
	8  Extra  额外信息

hashMap的put方法:

jdk7采用数组加链表的形式,采用头插法.jdk8采用数组链表和红黑树的形式,采用尾插法,简化了hash算法,节省了cpu资源.
首先用hashcode的哈希算法计算出存储key_value的索引,如果索引位置是空直接插入对应的数组中,否则,判断是否是红黑树,是插入,否则是链表,判断长度是否大于8,是则转为红黑树进行插入.

jdk1.7到jdk1.8的java虚拟机发生了什么变化

1.7中存在永久代,1.8中没有永久代.替换他的是元空间,元空间所占的不是虚拟机内部,而是本地内存空间.方法区所存储的类信息通常难以确定,所以对于方法区的大小不好确定,转移到本地后不会影响虚拟机所占的内存

说一下ThreadLocal

ThreadLocal是java提供的本地存储机制,该线程可以在任意时刻任意方法中获取到缓存的数据.底层是通过ThreadLocalMap实现,每个Thread对象中都会存一个ThreadLocalMap,map的key为ThreadLocal对象,value是需要缓存的值.可能会造成内存泄漏,因为ThreadLocal对象使用完之后需要把key,value回收,而线程池中的线程不会回收,解决方法需要手动调用remove方法.应用场景,一个线程连接一个对象.

jvm中哪些是共享区哪些是gc root

堆区和方法区是所有线程共享的,,本地方法栈,程序计数器是每个线程独有的.

如何排查jvm问题

对于还在正常运行的系统:
1.可以使用jmap来查看jvm中各个区域的使用情况
2.可以通过使用jstack来查看线程的运行情况,比如哪些线程阻塞、是否出现了死锁
3.可以通过jstat命令来查看垃圾回收的情况,特别是fullgc,如果发现fullgc比较频繁,那么就得进行调优了
4.通过各个命令的结果,或者jvisualvm等工具来进行分析
5.首先,初步猜测频繁发送fullgc的原因,如果频繁发生fullgc但是又一直没有出现内存溢出,那么表示fullgc实际上是回收了很多对象了,所以这些对象最好能在younggc过程中就直接回收掉,避免这些对象进入老年代,对于这种情况,就要考虑这些存活时间不长的对象是不是比较大,导致年轻代放不下,直接进入了老年代,尝试加大年轻代的大小,如果改完之后,fullgc减少,则证明修改有效
6.同时,还可以找到占用CPU最多的线程,定位到具体的方法,优化这个方法的执行,看是否能避免某些对象的创建,从而节省内存

对于已经发生OOM的系统:
1.一般生产系统中都会设置当系统发生OOM时,生成当时的dump文件(-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base)
2.我们可以利用jvisualvm等工具来分析dump文件
3.根据dump文件找到异常的实例对象,和异常的线程(占用CPU高),定位到具体的代码
4.然后再进行详细的分析和调试

内存泄漏和内存溢出

内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。

内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出。

线程间通信的几种实现方式:

共享内存和消息传递,基于 volatile 关键字来实现线程间相互通信是使用共享内存的思想,大致意思就是多个线程同时监听一个变量,当这个变量发生变化的时候 ,线程能够感知并执行相应的业务。这也是最简单的一种实现方式.
Object类提供了线程间通信的方法:wait()notify()notifyaAl(),它们是多线程通信的基础

spring的大致流程

spring是一个快速开发框架,管理对象.
在创建spring容器的时候,首先会进行扫描,扫描所有的bean对象,并保存在一个map中,然后筛选出非懒加载的到哪里beandefinition进行创建,单例bean创建完之后,spring会发布一个容器启动时间,结束.

spring事务机制

1 spring事务底层是基于数据库事务和aop机制的
2  首先对于使用了@transactional 注解 的bean,spring会创建一个代理对象作为bean
3 当调用代理对象的方法时,会判断是否加上@transactional 注解
4 如果加了,则会利用事务管理器创建一个数据库连接
5 并且修改数据库连接的autocommit属性为false,禁止次连接的自动提交,这是实现spring事务的重要一步
6  然后执行此方法,方法中会执行此sql
7 执行完当前方法时,如果没有出现异常就会直接提交事务
8  如果出现异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务
9 spring事务的隔离级别就是数据库的隔离级别
10 spring的事务传播机制是spring事务自己实现的,也是spring事务中最复杂的
11 spring事务传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为需要新开一个事务,那实际上就是先建立一个数据库连接,在此连接上执行sql

什么时候@transactional会失效

1 @Transactional注解只能应用到public修饰符上,其它修饰符不起作用,但不报错。
2 默认情况下此注解会对unchecked异常进行回滚,对checked异常不回滚。继承自Exception的异常统称为checked异常,如 IOException、TimeoutException等。
3 一般在service里加@Transactional注解,不建议在接口上添加
4 同一个类中方法调用,导致@Transactional失效。比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。

Dubbo的负载均衡策略:

平衡加权轮询算法,
加权随机算法
一致性哈希算法
最小活跃算法

AOP实现原理

面向切面,动态代理,jdk动态代理,
bean的实例化过程,即调用构造函数将对象创建出来
bean的初始化过程,即填充bean的各种属性

spring中后置处理器的作用

spring中后置处理器分为beanfactory后置处理器和bean后置处理器.他们是spring底层源码架构设计的非常重要的一种机制,spring启动过程中,会先创建beanfactory实例,然后beanfactory处理器来加工beanfactory,

常用springboot注解和实现:

@springbootApplication注解:这个注解标识了一个springboot工程,实际上是三个注解的组合:
a @sprinhbootconfiguration:这个注解实际上是一个@configuration,表示启动类也是一个配置类.
b:enableAutoConfiguration:向spring容器中导入一个selector,用来加载classpath下的springFactories中所定义的自动配置类,将这些自动加载为配置bean
c: @configuration 标识扫描路径,因为默认是没有配置实际扫描路径,所以springboot扫描的路径是启动类所在的目录
2 @bean 用来定义bean,类似xml总的<bean>标签,spring在启动时,会对加了@bean注解的方法进行解析,将方法的名字作为beanname,并通过执行方法得到bean对象
3 @controller @service@responseBody@autowired

分布式锁:

分布式锁哟啊解决的本质问题:能够对分布在多台机器中的线程对共享资源的互斥访问,在这个原理上有很多实现方式:
1 基于Mysql,分布式环境中的线程连接的是同一个数据库,利用数据库中的行锁来达到互斥访问,但是Mysql的加锁和释放锁的性能会比较低,不适合真正的实际生产环境
2 基于zookeeper,zk中的数据是存在内存中的,所以相对于Mysql性能上是适合实际环境的,并且基于zk的顺序节点,临时节点,watch机制能非常好的来实现分布式锁
3 基于redis,redis中的数据也是在内存中,基于redis的消费订阅功能,数据超时时间,lua脚本功能,也能很好饿实现分布式锁

redis的数据结构:

字符串:可以用来做最简单的数据缓存,可以缓存某个简单的字符串,也可以缓存某个json格式的字符串,redis分布式锁的实现就是利用这种数据结构,还包括可以实现计数器,session共享,分布式ID
哈希表: 可以用来存储一些key-value对,更适合用来存储对象.
列表: redis的列表通过命令的组合,既可以当做栈,也可以当做队列来使用,可以用来缓存类似微信公众号,微博等消息流数据.
集合:和列表类似,也可以存储多个元素,但是不能重复,集合可以进行交集,并集,搽剂操作,从而可以实现类似,我和某人共同关注的人,朋友圈点赞等功能
有序集合: 集合是无序的,有序集合可以设置顺序,可以用来实现排行榜功能.

redis集群策略:

1 主从模式: 这种模式比较简单,主库可以读写,并且与从库进行数据同步,这种模式下,客户端直接连主库或者某个从库,但是当主库或从库宕机后,客户端需要手动修改ip,另外,这种模式也比较难进行扩容,整个集群所能存储的数据受到某台机器的内存容量,所以不可以支持特大数据量.
2 哨兵模式: 这种模式在中的基础上新增了哨兵节点,但主库节点宕机后,哨兵会发现主库节点宕机,然后在从库中选择一个库作为新的主库,另外哨兵也可以做集群,从而可以保证但某一个哨兵节点宕机后,还有其他哨兵节点可以继续工作,这种模式可以比较 好的保证redis集群的高可用,但是仍然不能很好的解决redis的容量上限问题.
3 cluster模式,cluster模式是用的比较多的模式,他支持多主多从,这种模式会按照key进行槽位的分配,可以使得不同的key分散到不同的主节点上,利用这种模式可以使得整个集群支持更大的数据容量,同时每个主节点可以拥有自己的多个从节点,如果该主节点宕机,会从他的从节点中选举一个新的主节点.
对于这三种模式,如果redis要存的数据量不大,可以选择哨兵模式,如果redis要存的数据量大,并且需要持续的扩容,那么选择cluster模式.

mysql什么情况下设置了索引但是无法使用?

1 ,没有符合最左前缀原则
2  字段进行了隐私数据类型转化
3  走索引没有全表扫描效率高
Where 条件中 not in 和 <>操作无法使用索引;
联合索引中,如果查询中有某个列的范围查询,则其右边的所有列都无法使用索引;
比如说:有个[age,name]联合索引, where age>18 and name='abc’这是用不到索引的,它只会age使用索引,name不会使用索引。还是基于最左匹配原则
索引列的数据长度能少则少;
索引一定不是越多越好,越全越好,一定是建合适的.`

innoDB事务和锁

**事务**:(mysql默认打开事务) 当前操作要么全部成功,要么全部失败。这样可以简化错误恢复并使应用程序更加可靠
四大特性:
	1 原子性:事务中所有操作要么全部完成要么全不完成
	2 一致性:事务使数据库中一个一致性状态变成另一个一致性状态
	3 隔离性: 一个事务的执行不被其他事务干扰
	4 持久性: 一个事务一旦提交,对数据库数据的改变是永久性的,... 一致性这个概念不好理解,举个栗子。eg: 比如银行有2000块,A1020,B1000。A转账100给B,中间出现异常,也不会影响银行2000块这个总数的不一致,不会出现A:1100 、 B 1000 这种情况。
 事务并发带来的问题:1 脏读:一个事务对数据进行了增删改但是没有提交,另一个事务可以读取到未提交的数据,如果第一个事务进行了数据回滚,第二个事务就会读到脏数据
 	2 不可重复读: 一个事务中发生了两次读操作,在两次读操作之间,另一个事务对数据进行了修改,第一个事务两次读到的数据是不一样的
 	3 幻读: 第一个事务对一个范围的数据进行批量查询,第二个事务在这个范围增加了一条数据,这个时候第一个事务会丢失对新增数据的内容.
事务的四种隔离级别:
	1 读未提交  导致三种问题
	2 读已提交  解决脏读
	3  可重复读(mysql默认)  除innodb外,其他引擎解决不了幻读
	4 串行化  都可以解决,但是效率很慢
	
****
	1  共享锁(S锁)只能读不能修改
	2  排他锁 (行级锁,未命中索引会退变为表锁) 
	3 意向共享锁(IS锁) 表锁
 	4   意向排他锁(IX锁)表锁  意向锁相当于 Java 中的 flag,就是一个标记,在加锁前会判断该表是否有锁
  	5  自增锁  表级别锁
  	6  临键锁 & 间隙锁 & 记录锁  行锁 
  	7  临键锁 (InnoDB引擎默认行锁算法)

InnoDB如何实现事务的?

Innodb在接收到一个update语句后,会先根据条件找到数据所在的页,并将该页缓存在buffer pool中,
执行update语句修改buffer pool中的数据,也就是内存中的数据
针对update语句生成一个redolog对象,并存入logbuffer中
针对update语句生成undolog日志,用于事务回滚,
如果事务提交,则吧redolog对象进行持久化,后续还有其他机制将buffer pool中所修改的数据页持久化到磁盘总
如果事务回滚,则利用undolog日志进行回滚

遇到过哪些代理模式

在学习一些框架和底层源码 的时候遇到过一些设计模式:
1 代理模式,mybatis中用到jdk动态代理生成mappper的代理对象,再执行代理对象的方法时会去执行sql,spring中的aop.包括@configuration注解的底层实现也都用到了代理模式.
2 责任链模式  Tomcat中的pipeline实现,以及dubbo中的filter机制都使用责任链模式
3 工厂模式  spring中的beanfactory就是一种工厂模式
4 适配器模式 spring中的bean销毁的声明周期中用到适配器模式,用来适配各种bean销毁逻辑的执行方式
5 外观模式  Tomcat的request和requestFacade之间体现的就是外观模式
6 模板方法模式  spring中的refresh方法提供给子类继承重写的方法,

java死锁如何避免

造成死锁原因:
1 一个资源每次只能被一个线程使用
2 一个线程在阻塞等待某个资源时,不得释放已占有资源
3 一个线程已经获得的资源,在未使用完之前,不能被强行剥夺
4 若干线程形成头尾相接的循环等待资源关系
前三个是作为锁需要的条件,所以要尽量避免第四点,
在开发过程中,要注意加锁顺序,保证每个线程按同样的顺序进行加锁,
哟啊注意加锁时限,可以针对锁设置一个超时时间
要注意死锁检查,这是一种预防机制,确保在第一时间发现死锁并进行解决.

深拷贝浅拷贝

浅拷贝是指,只会拷贝基本数据类型的值,和实例对象的引用地址,并不会复制一份引用地址所指向的对象,也就是内部的类属性指向同一个对象.
深拷贝是指即会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,深拷贝出来的对象,内部类执行指向的不是同一个对象.

如果你提交任务时,线程池队列已满,这时会发生什么

如果使用的无界队列,那么可以继续提交任务时没有关系的
如果使用的有界队列,提交任务时,如果队列满了,如果核心线程数没有达到上限,那么则增加线程,如果线程数已达到最大值,则使用拒绝策略进行拒绝

谈谈concurrentHashMap的扩容机制

1.7版本:
	1 1.7版本的concurrentHashMap是基于segment分段实现的
	2 每个segment相对于一个小型的HashMap
	3  每个segment内部会进行扩容,和HashMap的扩容逻辑类似
	4 先生成的新的数组,然后转移元数据到新数据中
	5 扩容的判断也是每个segment内部单独判断的,判断是否超过阈值
1.8版本:
	1 1.8版本的concurrentHashMap不在基于segment实现
	2 当某个线程进行put时,如果发现concurrentHashMap正在进行扩容那么该线程一起进行扩容
	3 如果某个线程put时,发现没有正在进行扩容,则将key-value添加到concurrentHashMap中,然后判断是否超过阈值,超过则进行扩容
	4 concurrentHashMap支持多个线程同时及你行扩容
	5 扩容之前也先生成一个新的数组
	6 在转移元素时,先将原数组分组,将每组分给不同的线程来进行元素的转移,每个线程负责一组或多组的元素转移工作

spring中的bean是不是线程安全的

spring本身并没有针对bean做线程安全的处理,所以:
	1 如果bean是无状态的,安全
	2 有状态,不安全
另外,bean是不是线程安全跟bean的作用域没关系,bean饿作用域只表示bean生命周期,是否是线程安全还得看bean对象本身

常用的Linux基本操作命令

01  ls/ll			查看当前文件夹下的内容
02	pwd				查看当前所在文件夹
03	touch[文件名]	如果文件不存在,新建文件
04	mkdir[目录名]	创建目录 //-p可递归创建目录
05	rm[文件名]		删除指定的文件
06	cd[目录名]		切换文件夹
07	cp[文件名]		拷贝指定的文件 //拷贝文件夹用-r
08	mv[文件名]		移动指定文件//移动文件夹不用加-r
09	tree[目录名]	以树状方式显示目录结构
10	clear			清屏

编辑文件

使用vi编辑器打开文件后点击按键:i ,a或者o即可进入编辑模式。

i:在光标所在字符前开始插入
a:在光标所在字符后开始插入
o:在光标所在行的下面另起一新行插入

保存或者取消编辑

保存文件:

第一步:ESC  进入命令行模式
第二步::     进入底行模式
第三步:wq     保存并退出编辑

取消编辑:

第一步:ESC  进入命令行模式
第二步::     进入底行模式
第三步:q!     撤销本次修改并退出编辑

文件的查看命令:cat/more/less/tail
权限: rwx:r代表可读,w代表可写,x代表该文件是一个可执行文件,如果rwx任意位置变为-则代表不可读或不可写或不可执行文件。

打包:命令:tar -zcvf 打包压缩后的文件名 要打包的文件
解压:  tar -xvf ab.tar -C /usr------C代表指定解压的位置

 关闭防火墙
命令:chkconfig iptables off
ifconfig:查看网卡信息
结束进程 命令:kill pid 或者 kill -9 pid(强制杀死进程)     
查看进程:ps -ef

springCloud各组件的功能和dubbo的区别

1 Eureka:注册中心,用来进行服务的自动注册和发现
2 Ribbon: 负载均衡组件,用来在消费者调用服务时进行负载均衡
3 Feign:基于接口的申明式的服务调用客户端,让调用变得简单
4 Zuul: 服务网关,可以进行服务路由,服务降级,负载均衡等
5 Nacos: 分布式配置中心以及注册中心
7 Seata: 分布式事务
8 springcloud config:分布式配置中心
9 springcloud bus:消息总线
springcloud是一个微服务框架.提供微服务领域的很多功能组件,dubbo一开始是一个rpc调用框架,核心是解决服务调用问题,springcloud是大而全,dubbo更侧重服务调用,

泛型中extends和super的区别

<extends T>表示包括T在内的任何T的子类
<super T>表示包括T在内的任何T的父类

并发编程三要素

1 原子性L不可分割的操作,多个步骤要保证同时成功或者同时失败
2 有序性:程序执行的顺序和代码的顺序保持一致
3 可用性: 一个线程对共享变量的修改,另一个线程能马上看到

简书CAP理论:

CAP理论是分布式领域非常重要的一个理论,很多分布式中间件在实现都需要遵守这个理论,
1 C表示一致性:指的是分布式系统中的数据一致性,
2 A表示可用性,表示分布式系统是否正常可用
3 P表示分区容器性 :表示分布式系统出现的网络问题时的容错性.
CAP理论是指,在分布式系统中不能同时保证C和A,也就是说在分布式系统中要么保证CP,要么保证AP,也就是一致性和可用性只能取其一,如果想要数据一致性,那么就需要损失系统的可用性.
BASE理论: 分布式系统不需要保证数据的强一致,只要做到最终一致,也不用保证一直可用.

快排算法

快排算法采用分治法思想,先取列第一个数作为基准数,将数列比基准数大的放右边,比基准数小的放左边,在左右两部分重负第二步,直到各区间只有一个数

TCP的三次握手和四次挥手

TCP协议是7成网络协议中的传输协议,负责数据的可靠传输,在建立TCP连接的时候,需要通过三次握手来建立连接,
1 客户端向服务端发送一个SYN
2 服务端接收到SYN后,给客户端发送一个SYN_ACK
3 客户端接收到SYN_ACK后,再给服务端发送一个ACK
在断开TCP连接时,需要通过四次挥手来:
1客户端向服务端发送FIN
2 服务端接收FIN后,向客户顿发送ACK,表示接收到可以断开连接的请求,客户端 可以不发数据了,服务端还是需要继续处理剩余数据
3 服务端处理完所有数据之后,向客户端发哦是哪个FIN,表示服务端可以断开连接
4 客户端收到服务端的FIN,向服务端发送ACK,表示客户端也会断开连接

消息队列中如何保证消息可靠传输

消息可靠传输代表了两层意思,既不多也不能少
1 为了保证消息不重复,也就是生产者不能重复生产消息,或者消费者不能重复消费消息.
a 避免不重复消费,最保险的机制就是消费者首先幂等性,保证就算是重复消费,也不会有问题,通过幂等性,可以解决生产者重复发消息的问题
2 消息不丢失
a 生产者发送消息时,要确认broker确实收到并持久化这条消息,比如rabbitmq的confirm机制,kafka的ack机制都可保证生产者能正确的将消息发送到broker
b broker要等到消费者真正的将消息消费了才能删除消息,这是通常的ack机制.

二叉树和平衡二叉树的关系

平衡二叉树也叫平衡二插搜索树,二插搜索树是左边所有节点都比改该点小,节点右边都比该节点大,平衡二叉树规定左右两边的子树高度差的绝对值不能超过1

强平衡二叉树和弱平衡二叉树有什么关系

强平衡二叉树是AVL树,弱平衡二叉树是红黑树
1 相同节点,AVL树的高度哟啊低于红黑树
2 红黑树中增加一个节点颜色的概念
3 AVL树的旋转操作比红黑树的旋转操作更耗时

B树和B+树的区别,为什么mysql使用B+树

B树特点
1 节点排序
2 一个节点可以存储多个元素,多个元素也排序了
B+树的特点:
1 拥有B树的特点
2 叶子节点之间有指针
3 非叶子节点上的元素在叶子节点上都冗余了,也就是叶子节点存储 所有的元素,并做好排序
mysql索引使用的B+,因为索引是用来加快查询的,而B+树通过对数据的排序所以是可以加快查询速度的,然后通过一个节点中可以存储的多个元素,从而可以使得B+树高度不会太高,在mysql中一个innodb页默认是16kb,所以在一般情况下一颗两层的B+树可以存储2000万行左右的数据,然后通过B+树叶子节点存储所有的数据并且进行排序,并且叶子节点之间是有指针,可以会很好的扫描全表.范围查找等sql语句.

线程池原理

线程池内部是队列+线程实现的,当我们利用线程池执行任务时,
1 如果此时线程池的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务
2 如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放进去缓冲队列
3 如果此时线程池中的数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务
4 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池的数量等于maximumPoolSize,那么通过handle所指定的策略来处理此任务,
5 如果此时线程池中的数量大于corePoolSize,并且某线程空闲时间超过keepAliveTime,线程被终止,这样,线程池可以动态调整池中的线程数.

sychronized 和reentrantLock区别

   1 sychronized是一个关键字,reentrantLock是一个类
   2 sychronized会自动加锁和释放锁,reentrantLock需要手动加锁和	    					     释放锁
   3 sychronized的底层是jvm层面的锁,reentrantLock是api层面的锁
   4 sychronized是非公平锁,reentrantLock可以选择公平锁或非公平锁
   5 sychronized锁是对象,锁信息保存在对象头中,reentrantLock通过代码中int类型的state标识锁饿状态
   6 sychronized底层有一个锁升级过程

sychronized的自旋锁,偏向锁,轻量级锁,重量级锁,分别介绍和联系

 1 偏向锁:在锁对象的对象头中记录一下当前获取该锁的线程id,该线程下次如果又来获取锁就可以直接获取到
 2 轻量级锁:由偏向锁升级而来,当一个线程获取锁之后,此时这把锁是偏向锁,此时如果有第二个线程来竞争锁,偏向锁就会升级为轻量级锁,轻量级锁底层是通过自旋来实现,并不会阻塞线程
 3 如果自旋次数过多还没有获取到锁,升级重量级锁,重量级锁会导致线程阻塞
 4 

hashMap的put方法

//put方法,会先调用一个hash()方法,得到当前key的一个hash值,
//用于确定当前key应该存放在数组的哪个下标位置
//这里的 hash方法,我们姑且先认为是key.hashCode(),其实不是的,一会儿细讲
public V put(K key, V value) {
	return putVal(hash(key), key, value, false, true);
}

//把hash值和当前的key,value传入进来
//这里onlyIfAbsent如果为true,表明不能修改已经存在的值,因此我们传入false
//evict只有在方法 afterNodeInsertion(boolean evict) { }用到,可以看到它是一个空实现,因此不用关注这个参数
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
			   boolean evict) {
	Node[] tab; Node p; int n, i;
	//判断table是否为空,如果空的话,会先调用resize扩容
	if ((tab = table) == null || (n = tab.length) == 0)
		n = (tab = resize()).length;
	//根据当前key的hash值找到它在数组中的下标,判断当前下标位置是否已经存在元素,
	//若没有,则把key、value包装成Node节点,直接添加到此位置。
	// i = (n - 1) & hash 是计算下标位置的,为什么这样算,后边讲
	if ((p = tab[i = (n - 1) & hash]) == null)
		tab[i] = newNode(hash, key, value, null);
	else { 
		//如果当前位置已经有元素了,分为三种情况。
		Node e; K k;
		//1.当前位置元素的hash值等于传过来的hash,并且他们的key值也相等,
		//则把p赋值给e,跳转到①处,后续需要做值的覆盖处理
		if (p.hash == hash &&
			((k = p.key) == key || (key != null && key.equals(k))))
			e = p;
		//2.如果当前是红黑树结构,则把它加入到红黑树 
		else if (p instanceof TreeNode)
			e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
		else {
		//3.说明此位置已存在元素,并且是普通链表结构,则采用尾插法,把新节点加入到链表尾部
			for (int binCount = 0; ; ++binCount) {
				if ((e = p.next) == null) {
					//如果头结点的下一个节点为空,则插入新节点
					p.next = newNode(hash, key, value, null);
					//如果在插入的过程中,链表长度超过了8,则转化为红黑树
					if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
						treeifyBin(tab, hash);
					//插入成功之后,跳出循环,跳转到①处
					break;
				}
				//若在链表中找到了相同key的话,直接退出循环,跳转到①处
				if (e.hash == hash &&
					((k = e.key) == key || (key != null && key.equals(k))))
					break;
				p = e;
			}
		}
		//①
		//说明发生了碰撞,e代表的是旧值,因此节点位置不变,但是需要替换为新值
		if (e != null) { // existing mapping for key
			V oldValue = e.value;
			//用新值替换旧值,并返回旧值。
			if (!onlyIfAbsent || oldValue == null)
				e.value = value;
			//看方法名字即可知,这是在node被访问之后需要做的操作。其实此处是一个空实现,
			//只有在 LinkedHashMap才会实现,用于实现根据访问先后顺序对元素进行排序,hashmap不提供排序功能
			// Callbacks to allow LinkedHashMap post-actions
			//void afterNodeAccess(Node p) { }
			afterNodeAccess(e);
			return oldValue;
		}
	}
	//fail-fast机制
	++modCount;
	//如果当前数组中的元素个数超过阈值,则扩容
	if (++size > threshold)
		resize();
	//同样的空实现
	afterNodeInsertion(e
vict);
	return null;
}

https是如何保证安全传输的

https通过使用对称加密,非对称加密,数字证书等方式来保证数据的安全传输
1 客户端向服务端发送数据前,需要先建立tcp连接,建立完连接后,服务端会先向客户端发送公钥,客户端拿到公钥后就可以用来加密数据 了,服务端到时候接收到数据就可以用撕咬进行解密数据,这种就是通过非对称加密来进行数据传输
2 不过非对称加密比对称加密要慢,所以不能直接使用非对称加密来传输请求数据,可以通过非对称加密来传输对称加密的秘钥,然后就可以使用对称加密来传输请求数据了
3 但是使用非对称加密和对称加密是不足以保证数据传输的绝对安全的,因为服务端向客户端发送公钥的时候可能被截取
4 所以为了安全的传输公钥,需要使用到数字证书,服务端向客户端发送公钥的时候,可以吧公钥和服务端相关信息通过hash算法生成消息摘要,在通过数字证书提供的私钥对消息摘要进行加密生成数字签名,在吧没进行hash算法之前的信息和数字签名一起生成数字证书,最后吧数字证书发送给客户端,客户端收到数字证书后,会通过数字证书提供的公钥来解密数字证书,从而得到非对称加密需要的公钥.

volatile关键字,是如何保证可见性和有序性

1 对于volatile关键字的成员变量,在对这些变量进行修改时,会直接将cpu高级缓存中的数据写回到主内存中,对这个变量的读取也会直接从主内存中读取,从而保证可见性
2 对于volatile修饰的成员变量进行读写时,会插入到内存中,而内存屏障可以达到禁止重排序的效果,从而达到保证有序性.

jvm内存结构

(https://blog.csdn.net/rongtaoup/article/details/89142396?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164136487616780271951658%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=164136487616780271951658&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-89142396.pc_search_result_control_group&utm_term=jvm%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84&spm=1018.2226.3001.4187)



JVM 的运行时数据区主要包括:堆、栈、方法区、程序计数器等。而 JVM 的优化问题主要在线程共享的数据区中:堆、方法区

mysql锁

按锁粒度分类:
1 行锁:锁某行数据,锁粒度最小,并发度高
2 表锁:锁整张表,所粒度大,并发度低
3 间隙锁:锁的是一个区间
共享锁:
 也就是读锁,其他事务只能读不能写
排他锁:
 写锁,给某行数据加写锁,其他事务不能读也不能写 
乐观锁:
 并不会真正去锁某行记录,而是通过一个版本号来实现
悲观锁:
 上面所有行锁表锁都是悲观锁

Spring Boot 的核心注解

启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:

@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文
件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动
配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude{DataSourceAutoConfiguration.class 
})@ComponentScan:Spring组件扫描。

Spring Boot 自动配置原理

注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,

@EnableAutoConfiguration 给容器导入META-INF/spring.factories 里定义的自动配置类。

筛选有效的自动配置类。

每一个自动配置类结合对应的 xxxProperties.java 读取配置文件进行自动配置功能

Spring Security 和 Shiro 各自的优缺点

由于 Spring Boot 官方提供了大量的非常方便的开箱即用的 Starter ,包括 Spring Security 的 
Starter ,使得在 Spring Boot 中使用 Spring Security 变得更加容易,甚至只需要添加一个依
赖就可以保护所有的接口,所以,如果是 Spring Boot 项目,一般选择 Spring Security 。当然
这只是一个建议的组合,单纯从技术上来说,无论怎么组合,都是没有问题的。Shiro 和 Spring 
Security 相比,主要有如下一些特点:

Spring Security 是一个重量级的安全管理框架;Shiro 则是一个轻量级的安全管理框架
Spring Security 概念复杂,配置繁琐;Shiro 概念简单、配置简单
Spring Security 功能强大;Shiro 功能简单

事务隔离级别自己的理解:

   读未提交,整个过程不加锁,读已提交,在执行完成后提交前释放死锁,
   可重复读在提交后释放死锁,串行化在几个操作加范围锁,全部执行完
   成并提交后释放锁.
   脏读:比如我给你转钱,但是没提交,你看到卡里显示已经有了,然后我
   回滚事务,不给你转了,最后你发现实际没到账.  不可重复读:我在商
   场买东西准备刷卡付钱,先查看了一下卡里有2000,然后你突然网上操
   作吧我卡里钱转走并提交完成,我付款时候发现没钱了.   幻读:我查 
   看你的卡里有100块消费记录,准备去打印,然后你刚好支付了1000,然 后我打印出来有两笔消费记录..
   

三级缓存自己的理解:

   如果没有循环依赖的时候只用一级缓存就行,有循环依赖的时候才用到二三级缓存.
   假如初始化A bean时,发现A bean依赖B bean,即A初始化执行到了第3步
填充属性,需要注入B bean,此时B还没有初始化,则需要暂停A,先去初
始化B,那么此时new出来的A对象放哪里,直接放在容器Map里显然不合
适,半残品怎么能用,所以需要提供一个可以标记创建中bean(A)的Map,
可以提前暴露正在创建的bean供其他bean依赖,而如果初始化A所依赖的
bean B时,发现B也需要注入一个A的依赖(即发生循环依赖),则B可以从
创建中的beanMap中直接获取A对象(创建中)注入A,然后完成B的初始
化,返回给正在注入属性的A,最终A也完成初始化,皆大欢喜。

synchronized和Lock的区别

面试常见问题一二_第1张图片

堆和栈区别

堆中存放的是实体对象,并且在使用完之后不会自动释放,由垃圾回收机制
进行回收,栈中存放局部变量,生命周期结束会自动释放,所以他的更新速
度快于堆内存.

volatile和synchronized的区别

volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需
要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变
量,其他线程被阻塞住。
volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别
的
volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保
证变量的修改可见性和原子性
volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

你可能感兴趣的:(java,全文检索,java,mysql)