- 请写出一个单例模式 (饿汉式 懒汉式)
- 请写出一个生产者消费者模型,
wait notify
等待通知, 加锁 threadLocal
是干什么用的, 是 线程内独享变量,但在方法和类中共享mybatis
运行流程, 创建sqlSessionFactory
对象,生成sqlSession
, 进行设置参数 ,执行,到结果处理器,resultSetHandler
,进行映射dubbo
里的参数,通过spring
初始化最后一步调用 一监听器, 然后调用export
接口 进行暴露dubbo
里的参数,重试次数, 过期时间
redis
缓存如果太多,内存不够,那么可以实例化到硬盘中, redis有6种过期策略 lru, ttl, redis哨兵模式
spring mvc
流程,是通过dispatcherServlet
到HandlerMapping
映射handlerAdaptter
到controller
, 返回视图modelandview
或者 json串, 通过dispatherSerlvet
返回给前台界面mybatis
一级缓存sqlSession
二级缓存sqlSessionFactory
nginx
负载均衡配置 可以设置weight
权重比 并且使用keepalived
- 线程的状态
新建 可运行 运行 阻塞 死亡
- 线程的几种实现方式, 继承
Thread
实现Runnable
以及使用线程池 ,
线程池有四种, 可缓存线程池, 单条线程池, 定量线程池 - 大数据量 如何limit
可以使用联合索引
, 或者select *from biao where vid >= (select vid from biao order by vid limit 1000000,1) limit 10
redis
有aof和 rdb
aof的有点就是 可以每秒同步,要求数据准确性比较高, rdb恢复快, 但是可能有数据不全 aof文件很大dubbo
服务失效剔除基于 zookeeper的 临时节点的原理
countdownlatch
cycliebarriy
samephore
synchronized
锁原理,是非公平锁,可重入锁
, 原理是 进入锁的时候会有一个 monitorenter 对象生成
, 退出锁 会有monitorExit对象
, 缺点是加锁的时候 不可中断, 解锁方式单一
, 不可以等待一段时间后 不去获取锁, 他会一直阻塞可重入锁, 乐观锁, 悲观锁, 非公平锁,自旋锁
- mysql 索引
innodb mysicam
innodb
支持事务
,muysicam不支持
innodb是行级锁, 另一个是表级锁,mysicam为什么比innodb查询快
,innodb需要缓存
,myisam 只缓存索引
,innodb是寻地址,是块到行
,myisam直接是offset 定位比innodb快
,innodb需要维护 MVCC Multi-Version Concurrency Control
- 讲一下红黑树
- 左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。如图3。
右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。如图4。
变色:结点的颜色由红变黑或由黑变红。
- j
dk动态代理是针对接口的
,cglib动态代理是针对类的
InvocationHandler 中的 invoke 调用方法
- 说说linux常用命令
ps -ef|grep tomcat
ls cd top make cp df -hl
- 常用算法
二分法,冒泡排序
- list里面有重复数组 ,如何把重复数据去除,
可以使用 Collections里面的一个list转化set的方法
, 直接去除 - map初始化
16 扩容是 2倍
list初始化 10 在1.8以后是 初始化空数组
, 添加数据时候第一次初始化为 10 扩容 1.5倍 - map底层数据结构
链表加数组
允许 null null
,采用enety数组来存储 key-value
实际上, 这个entry是链表,都有next ,指针指向下一个entry,解决hash冲突的问题,key如果为null 那么存在 0号位置,map是怎么保证key不重复, 是因为 key会生成hash值, 根据hash值判断元素在哪个区域,遍历这个区域就可以了
- list 遍历 remove的时候 会重新生成 list , 指针前移, 导致remove元素的时候 删除不全面
- 事务的四个特性 ACID
一致性,持久性,隔离性,原子性
- 说说常见的设计模式,
工厂模式,装饰器模式,代理模式,观察者模式,监听者模式,单例模式
- springboot 打jar包, 配置文件可以用
yml 或者 properties
@SpringBootApplication @Configuration
- sql优化, 可以使用
explain
来展示sql语句,是否用了索引,尽量少用 %like% 这样不会使用索引,
in少用 用exist替代
,少用or 这样会全表查询
, 可以使用联合索引, 从左到右 - mybaits 动态语句 where if foreach set
- tcp七层协议 应用层, 会话层, 表示层,传输层,网络层,数据链路层,物理层
- jvm内存模型,
java堆 ,java栈 ,本地方法栈, 常量池,程序计数器
java 堆分为年轻代, 年老代 ,持久代,
gc方法标记清除, 标记整理, 复制算法,
回收方法,串行gc , 并行gc 并发gc
cms - tomcat优化, 配置
maxThreads
, 以及占用内存大小 ,acceptCount
排队接受请求最大数量,最大连接数maxConnections
连接超时时间connectionTimeOut
- spring初始化
ClassPathXmlApplicationContext 根据xml实例化 applicationContext
- redis加锁怎么实现原子性
,是通过setnx 来实现的
- rabiitmq 里的参数 ,
exchange
交换机,queue
队列,bind
绑定,channl
消息读写通道 - rabbitmq是怎么保证消息不重复消费的? 保证幂等就可以
如果zookeeper挂了, dubbo还会运行 , 会 , dubbo有一个 缓存,可以通过缓存来取
hashmap和hashtable的区别
:hashmap
是线程不安全的, 效率高,hashtable
是线程安全的 ,效率低,hashmap
可以存储null key null value
hashtable
不可以- sleep和 wait的区别
sleep不会释放锁
,而 wait会释放
wait常用于线程交互, sleep是暂时停止执行
valtile 是保证变量的可见性, 以及防止指令重排序,
不具备原子性, synchronized可以保证原子性, 是在方法,变量, valtile只可以修饰变量,- j
ava原子类, atomicInteger 等等
,主要是用了 CAS 乐观锁, CAS缺点是 ABA , 可以使用 version字段来进行管控
int i = 8000; integer i2 = new Integer(8000); i == i2 不相等, integer 有个维护的 -128 - 127
ConcurrentHashMap
的读是否要加锁不需要加锁 可以使用valetile 来实现内存可见性,读取不是脏数据
- sql未使用索引有可能是
select * 不走索引
,在 索引字段上进行了 函数运算
,!= 有可能不走索引
也有可能走 INDEX FAST FULL SCAN
单字段有 null值有可能不走索引
rabbitmq 默认持久化队列 , 是 true
mysql联合索引 最左原则, c1 c2 c3 c4 这四个条件都有时候,可以使用索引, 如果 c2 是第二个 ,并且没有这个条件查询, 那么 只有 c1用, 不按顺序 c1 c2 c4 orderby c3 那么 c1 c2使用到索引, c4没有用到, 联合索引按照字段顺序, 必须是这个顺序才可以使用到, 联合索引符合最左原则,
索引的最左原则(左前缀原则),如(c1,c2,c3,c4....cN)的联合索引,where 条件按照索引建立的字段顺序来使用(不代表and条件必须按照顺序来写),如果中间某列没有条件,或使用like会导致后面的列不能使用索引。
String的 equals方法, 先判断是否地址值相等, 相等直接返回两个对象相等, 如果不相等,那么判断是否是String类型, 是 String类型, 那么依次比较这两个数组里的数据
- 重写 hashcode方法, 即使返回值都是 1 == 依然是false, 重写 equals方法,那么就判断对象的值是否相等 == 方法 是判断栈中的对象地址值是否相等,其实与hashcode关系不大 , 如果重写了 hashcode和 equals方法, 并且将这个对象存map中, map会认为是同一个对象,
-
}public static void main(String[] args) { Test test = new Test(); Test test1 = new Test(); System.out.println(test.equals(test1)); System.out.println(test == test1); System.out.println(test.hashCode()); System.out.println(test1.hashCode()); Map<String,Object> map = new HashMap<>(); map.put(test+"", test); map.put(test1+"", test); System.out.println(map.size());
@Override
public int hashCode(){
return 1;
}
@Override
public boolean equals(Object anObject) {
return true;
}
打印结果是
true
false
1
1
1
证明 map里调用和 hashcode和equals方法进行对比 , 如果equals方法返回false, 那么依然是 map里只有一个对象, 先对比的是hash值,
如果hash值相等, 不关注equals ,认为是一个对象
如果hash值不相等 ,equals值相等, 仍然认为是两个对象
-
java在 1.8以后 ConcurrentHashMap又变回了 synchronized 为什么? 1.加入多个分段锁浪费内存空间 2. map 在放入时竞争同一个锁的概率非常小,分段锁反而会造成更新等操作的长时间等待。 3.提高gc效率
put方法 , 如果是第一个object 那么 会直接用cas 进行添加, 如果 不是第一个object 那么synchronized 会用第一个object 当做锁,直到 重新计算map大小,或者扩容,或者操作了第一个object为止
, ConcurrentHashMap 初始化是 0 put的时候 判断是否是0 如果是0 那么进行初始化 -
分布式事务 二段式提交 原理 : 二段式提交事务,
1. 准备阶段, 协调员 发送 ABC参与者, 是否可以执行事务, ABC分别去执行操作, 如果操作成功,那么返回给协调员 yes, 可以进行事务提交
2. 提交事务 如果协调员 等待接收ABC信息超时,那么直接返回 abort ,回滚事务, 如果都是 yes, 协调员发送 commit 进行事务提交 ,
缺点,1. 协调员很重要,如果宕机,其他的都是在等待协调员,阻塞
2 .如果宕机之前,发送了 commit, 有一部分参与者接收到了,那么他们会提交事务,导致数据不一致
(解决方案 : CAP原则 强一致性, 可用性, 分区容错性
) -
分布式事务 有很多种实现方式
mq事务消息,Sagas 事务模型 把长事务分解为短事务, 并且如果事务回滚,那么会执行补偿操作, 二段式提交 三段式提交 TCC补偿事务
-
支付的时候 富有没有回调怎么办,
可以通过同步异步双重保障,监测在进行中的订单, 一段时间去富有那边去查询
, -
两个对象equals为真,那么hashcode一定为真 反之 hashcode相等,两个对象equals不一定相等
-
死锁产生条件 :
1. 互斥条件 2. 占有且等待 3.不可抢占 4.循环等待、
-
思路是创建两个字符串a和b,再创建两个线程A和B,让每个线程都用synchronized锁住字符串(A先锁a,再去锁b;B先锁b,再锁a),如果A锁住a,B锁住b,A就没办法锁住b,B也没办法锁住a,这时就陷入了死锁
-
怎样避免死锁? 1.
破坏循环等待条件 , 让线程有序执行
2.破坏不可抢占条件 一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到 系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行
3.破坏占有且等待条件
-
synchronized 和 ReentrantLock 区别是什么? 答 :
ReentrantLock 默认也是非公平锁 但是可以实现公平锁, synchronized 是非公平锁, ReentrantLock 使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行 所以 释放锁要卸载 finally 中
-
说一下 atomic 的原理?
volatile 来确定 该数据是最新的
但是没法保证原子性, 原子性 用 CAS + 自旋 来实现
-
Spring事务隔离级别和传播特性
5 大特性 1.默认 2.读未提交 3. 读已提交 4. 可重复读 5.序列化
7种事务传播方式 1. 当前如果有事务 支持 没有事务新建 PROPAGATION_REQUIRED 默认为这个
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.SUPPORTS) -
mybaits #{} ${}
有什么区别 ?#{} 是当作字符串处理
${} 不会当字符串处理
-
jvm gc 流程 : 当
Eden
中内存不够了之后
触发Minor GC
将 存活的 对象存入S1 区
, 第二次 Eden中内存不够的时候 触发Minor GC
将eden s1
中存活的对象存入s2
中 from 区 和 to区是相对的,哪个区有对象 哪个区就是 from -
对象进入老年代的4种情况 : 1. 进行 minor GC时候 年轻代存不下 , 直接放入 老年代中 2. 大对象直接进入老年代(这个值可以通过
PretenureSizeThreshold
这个参数进行设置,默认3M) 3.长期存活的对象将进入老年代 (默认15岁,配置参数-XX:MaxTenuringThreshold
) 4. 还有一种情况,如果在From空间中,相同年龄所有对象的大小总和大于From和To空间总和的一半,那么年龄大于等于该年龄的对象就会被移动到老年代
,而不用等到15岁(默认): -
full GC 就是 年轻代进入 年老代, 但是年老代没有足够的空间容纳, 所以触发full gc ,对整个 heap 进行 gc ,如果 gc过后还不够 jvm会抛出
OutOfMemoryError
-
hashMap
put原理 首先 根据key生成一个hash值,唯一,p = tab[i = (n - 1) & hash]
根据这个第一次确定进行存储位置确定,是存在 0 号位 还是 1号位,初始化值是 size 16 ,然后进行定位,map扩容是存第17个数据的时候,arraylist添加第11个元素进行扩容
,添加第二个元素的时候分为三种情况1. key值相等, 那么会替换value值 2. 如果是treeNode 存储了至少8个元素 , 那么使用红黑树添加 3.上面都不符合 , 那么使用链表,如果链表长度大于等于 8 ,执行treeifyBin方法,将链表转换为红黑树
get 原理是根据这个 key 的 hash值来get到数据 (h = key.hashCode()) ^ (h >>> 16) -
ArrayList 原理 : 初始化 10 1.8之后初始化为空数据,添加第一个时候会初始化
-
countdownlatch 可以配合线程池进行使用, 将任务进行分段分割,获取到所有数据之后在进行排序,或者组成对象返回给前台,这样就可以节省时间
-
private static ExecutorService executorService = Executors.newFixedThreadPool(20); 创建线程池,可复用线程池 20个