1.并查集是如何实现的?
通过构建森林实现.
1>一开始给每一个节点设置所在的数为自己的节点树
2>读取节点之间的联系,将存在关联的树合并为一个树
3>生成一个具有多个树 的森林
4>查找是否存在联系只需要查找 对应的节点是否在同一颗树中
2.为什么亲戚关系要使用并查集来查找而不使用图?
因为使用图来描述这种联系耗费的资源太大,很多无用的边。
3.优先队列(PriorityQueue)是基于什么数据结构实现的?
java中的优先队列是基于数组结构的二叉堆实现的
4.延迟队列(DelayQueue)是基于什么数据结构实现的?
是基于 优先级队列(二叉堆) + 可重入锁 + Comparable<Delay>对象
5.java 为什么要推出lock锁?
因为synchronized锁有缺点:1.粒度最小是对象,当一个线程不释放锁,其他线程都需要等待。这个时候当这个线程
因为io或其他原因被阻塞了,其他线程只能无限等待,很影响程序的效率,这个时候,为了让其他线程不无限期等待
,比如说等待一段时间或者响应中断,lock就实现了这个功能。
6.lock 的 condition作用?
类型 object 的wait 和 notify ,只是更灵活,notity只作用到注册到自己condition的上的
synchronized 是非公平锁。ReentrantLock 默认是非公平锁,但可设置为公平锁。
7.那线程通过Object.nofity() 和 Condition.signal() 被唤醒时是否是公平的呢?
先说结果,在Java 1.8 HotSpot下,两者都是公平的。
8.什么是leader-follower模式?
以饭店经营来举例子
饭店所有角色统一叫做员工,每个客人从来到走让同一个员工招待,不同的员工招待不同的客人
9.单Reactor多线程模式(反应器模式)?
饭店的员工一般都分角色: 服务员 ,接待员,厨师
假设有一个叫做A的人,固定做为饭店的接待员,来客人了就会分客人一个座位号,然后交给其他服务员,比如B去处理客人后续的
问题.这中间会涉及到一次线程切换。如果把A,B比作线程,客人当做任务,任务由A到B,会经过一次线程上下文切换
类似的有netty的线程模型,分为bossGroup 和 员工 workerGroup,但是有selector 和线程池等很多优化点.
10.DelayQueue的 leader-follower模式?
饭店变高端了,会有手机APP预约功能,例如客人预约30分钟以后来店,饭店播报系统通知员工A到饭店门口等,
A到门口发现没客人后就会在门口休息30分钟,此时又有客人预约10分钟后来店,此时A正在休息,饭店系统会通知
员工店门口接待,此时可能是员工A,也可能是其他员工,我们叫这位新员工X,如果此时正好有客人来,员工X会通知员工C
来接替自己,员工X则负责招待当前的客人,如果没客人来,员工X则和员工A一样先休息10分钟。
11.HashMap解决碰撞的散列结构?
哈希碰撞 index = key.hashcode() & (size-1)
拉链寻址 碰撞后 用链表存储 举例:hashmap
开放寻址 碰撞后 往后找空位存储 举例:threadLocal
合并散列 碰撞后 往后找空位存储,碰撞节点指向存储节点
杜鹃散列 划分成两个散列表,碰撞后 将冲突元素放到另外一个散列表
12.ArrayDeque实现堆扩容需要进行几次元素拷贝?
tail
head
tail
1
head
tail
2 1
head
head tail
1 2
ArrayDeque是通过数组实现的双端队列,扩容的时候为了保持先入先出的特性,所以需要分段拷贝元素. 2次,
扩容的时候为2的倍数,是为了保证在后续计算元素索引位置的时候可以进行与运算.保证各位都是1,相领元素之间只相差1
13.Java 为什么不推荐使用stack?
因为stack 继承了 vector 类,而vector类提供了可以在任何节点插入和删除元素,这并不符合栈的描述
14.hashmap负载因子的作用?扰动函数的作用?
负载因子越大,有限空间存放的元素越多,同时碰撞的概率也会增大
负载因子越小, 有限空间存放的元素越少,碰撞概率小
所以hashmap一般采用3/4作为负载因子
扰动函数 h = hashcode() ^(h>>>16) 充分随机或正大随机性
15.threadLocal解决哈希碰撞的散列算法?
threadLocal要解决的是线程内资源共享,一般用在全链路监控或者日志框架等
threadlocal是基于数组的开放寻址结构,采用斐波那契散列,因为它在有限空间内,对线程内的元素计算索引位置更加分散
而 hashmap为了降低元素碰撞采用的是扰动函数
斐波那契散列
private static final int HASH_INCREMENT = 0x61c88647;
@Test
public void test_idx() {
int hashCode = 0;
for (int i = 0; i < 16; i++) {
hashCode = i * HASH_INCREMENT + HASH_INCREMENT;
int idx = hashCode & 15;
System.out.println("斐波那契散列:" + idx + " 普通散列:" + (String.valueOf(i).hashCode() & 15));
}
}
16.hashmap链表转红黑树的条件
空间长度64,链表元素个数大于8,碰撞情况下会将链表转为红黑树
17.关于ArrayList和LinkedList插入1000万个元素性能对比
头插时,LinkedList性能高于ArrayList
中间和尾插,ArrayList 性能高于 LinkedList
ArrayList耗时在元素拷贝,LinkedList耗时在节点创建。
18.创建代理的方式
JDK : Proxy.newProxyInstance(); 需要提供接口类
CGLIB: Enhancer 底层基于asm字节码框架,比jdk实现方式快1.5到2倍
asm代理: 难度高,实现复杂
byte-buddy: 字节码操作类库,使用更加简单,相比jdk,cglib,byte-buddy在性能上具有一定优势
javassist: 字节码框架
19.volatile 关键字的作用
volatile是虚拟机提供了轻量级同步机制
各个线程会从主存拷贝一份数据对象都各自的线程内存
不加关键字的线程只会更新线程内存中的值不会修改主存中对应的数据,
反之加了则会修改同时通知其他线程同步
1.保证多线程可见,防止指令重排,不能保证原子性
2.synchornized既保证原子性,又保证可见性
20.Integer.toHexString("".hashCode()) 输出结果?
哈希的计算公式;s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] 当字符串为空时则不会通过公式计算哈希值,也就是0,那么0的 toHexString 16进制转换仍然是0。
21.哪些是公平锁的实现方式?
1>TicketLock 由一个原子对象维护当前号牌,一个threadlocal负责管理各个线程的好牌 由当前号牌的线程执行,
其他线程自旋等待,
缺点是所有线程都需要监听这个当前号牌对象的状态,多个线程都在读写同一个变量,缓存同步起来很消耗性能
2>spin lock 自旋锁 随机
3>CLH lock 存在首位两个指针,中间维护一个线程链表
阻塞节点会监听上一个节点的flag状态,默认为true,表示当前线程正试图占有锁
当为false时,表明上一个线程已经不需要了,则当前线程显示运行
4>MCS lock
CLH lock并不完美,因为每一个节点都在频繁自旋上一个线程节点的状态,可能会有性能问题。
mcs lock则是在自己的节点上自旋,检测自己的节点状态
原来是 多次对远端内存的监听+一次对本地内存的更新 简化成了多次对本地内存的监听 + 一次对远端内存的更新
22.Synchronized 和 ReetrantLock的描述
1>两者都是独占锁,只允许线程互斥的访问临界资源
Synchronized是基于jvm层面的 而ReetrantLock是基于java层面的
23.Thread.start()的启动过程包括?
java创建线程和启动-》调用本地方法start0-》JVM中JVM_StartThread的创建和启动-》设置线程状态等待被唤醒
-》根据不同的OS启动线程并唤醒 -》 回调run方法启动java线程
24.thread的线程状态包括?
New: 新创建状态
RUNNABLE: 可运行状态
BLOCK:阻塞状态
WAITING:等待状态
TIME_WAITING:休眠等待状态
TERMINATED: 结束状态
Object 中的wait 和 notify 必须配合Synchronized使用,不然会报异常
thread.join()不是两个线程的合并,首先join() 是一个synchronized方法, 里面调用了wait(),让当前线程(即主线程)进入wait状态,等待子线程执行完后会被唤醒
25.线程池的拒绝策略?
1>抛异常拒绝 AbortPolicy
2>直接丢弃 DiscardPolicy
3>丢弃存活时间最长的任务 DiscardOldestPolicy
4>谁提交谁执行 CallerRunsPolicy
26.线程池中的worker为什么不用ReetrantLock实现,而要继承aqs?
work继承了 aqs ,用的是非公平锁, 独占锁,不可重入
因为线程池中的worker设计需要使用不可重入锁.为了防止线程在执行任务时被线程池操作类setPoolSize 获得锁.造成中断,把正在运行的任务停掉
ReentrantLock是可重入锁不支持
27.死锁的四个必要条件?
1>资源互斥,一个资源同一时间只能被一个线程占有
2>不可剥夺
3>请求与等待
4>存在闭环
28.JDK常用命令?
jps : 查看虚拟机进程状况 jps -v 显示启动参数
jcmd : 虚拟机诊断命令 可以用来导出堆和线程信息,查看java进程启动参数,运行时长,性能相关等信息,执行gc
jinfo : java 配置信息工具
jstat : 收集虚拟机运行统计数据
jmap : 内存映射工具 -dump 生成heapdump快照文件
jhat : 堆转储快照分析工具 与jmap配合使用,用处不大,一般会使用第三方工具
jstack : java 堆栈跟踪工具 用于生成虚拟机当前时刻
29.arthas常用命令
dashboard thread dump watch
30.JVM虚拟机运行时数据区包括?
运行时数据区可以分为2类:
1.多线程共享:java虚拟机启动时创建,java虚拟机关闭时销毁
堆 和 方法区(元空间)
2.线程私有:线程创建时创建,线程退出时销毁
pc寄存器(程序计数器)
栈帧(局部变量表,操作数栈)
31.JVM 包含哪几部分?
堆 存放new 的对象
栈 存放线程信息,战阵
方法区 存放静态变量,常量,类信息
本地方法栈 存放调用c/c++的哪些引用地址
程序计数器 引用计数,主要存地址
类装载系统
字节码执行引擎
32.堆由哪几部分构成?
由新生代和老年代组成,新生代占1/3,老年代占2/3
新生代: eden(伊甸园区) : s1 (幸存者1) : s2(幸存者2) 8:1:1
老年代: 老年代区
33.jvm如何找到垃圾对象?
一般由两者算法:
可达性分析算法:从GCROOT节点出发,向下搜索引用对象,能搜到的都是非可回收对象,其他的都是可回收对象
GCROOT 根节点:线程的本地变量,静态变量,本地方法栈的变量
引用计数法:引用就+1,不引用就-1
java用的是可达性分析算法,因为引用计数没有解决循环依赖的问题
34.jvm调优的目的是什么?
jvm调优的目的是减少fullgc,即减少STW的次数,因为做fullgc的时候,会停掉所有用户线程,专心去搞垃圾回收
对用户来说就会卡顿
minorgc也会导致卡顿,只是minorgc的时间很短,fullgc的时间很长
老年代满了才会做fullgc,如果fullgc后还放不下,就会报oom的错
35.常见的垃圾回收算法?
标记清除法 会产生碎片
标记整理 没有碎片,效率偏低
复制算法 没有碎片,会浪费空间,只用其中一部分,另一部分用作垃圾回收
36.常用的垃圾收集器?
以前是分代模型:
serial - serialOld 单线程收集
parallel - parallel old 多线程收集
parNew - cms 初始标记 -> 并发标记 -> 重新标记 -> 并发清理
现在是分区模型:
G1
ZGC
37.何为性能调优?
1.根据项目需求进行JVM规划和预调优
2.优化运行程序中JVM导致的卡顿现象
3.解决JVM运行过程中出现的各种问题(内存溢出)
38.spring核心启动流程?
运行-》加载xml或者class-》BeanDefinition -》BeanFactoryPostProcessor(提供的更新beanDefine的扩展机制)
-》准备初始化bean
-》BeanPostProcessor 的 postProcessBeforeInitialzation
-》initialingBean 的 afterPropertySet方法
-》BeanPostProcessor 的 postProcessAfterInitialzation
-》Bean准备就绪
-》调用DisposeableBean的destory方法
-》结束
39.spring为什么要使用三级缓存?
优雅。
三级缓存为了更好更优雅的处理aop工厂对象的创建
二级缓存就能优雅解决循环依赖问题,也可以处理工厂对象,但是需要包一层判断,不优雅
一级缓存能解决循环依赖,但是当调用实例化但未初始化的对象会报错。
40.factorybean 和 beanfactory的区别?
beanFactory 是生成bean的工厂,负责生成各种bean,
factoryBean 是 工厂bean,负责生成复杂的bean对象,这个复杂只是的不是new出来的对象。需要一些包装和
代理操作
41.mybatis缓存的作用范围?
一级缓存作用是在SqlSession会话层面的,一次会话结束(commit,rollback,close)后结束了
二级缓存作用到namespace范围,通过装饰一级缓存,把数据保存到队列中进行使用。
会话commit,close后,将一级缓存存入队列。
42.常见的缓存策略?
LIFO 先进先出
LRU 最近最少使用(最长时间)
LFU 最不经常使用(最少次)淘汰算法
SOFT - 软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK - 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
43.常见的设计模式,讲讲使用案例?
工厂模式 beanFactory
代理模式 aop
模板方法 context refresh
策略模式 DefaultResource classpath,Resources,URLResource
观察者模式 spring event 机制
适配器 InputStream InputStreamReader
装饰器 InputStream FiledInputStream BufferedInputStream
mybatis 二级缓存是由一级缓存装饰而成
44.设计模式原则?
开放封闭原则: 对扩展开放,对修改封闭
单一职责: 一个类,应该仅有一个引起它变化的原因,也就是干一件事
依赖倒置原则: 程序要依赖于接口抽象,而不是依赖于接口实现
迪米特法则(最小知道原则): 一个软件实体应尽可能少的与其他实体发生互相作用
接口隔离原则
里氏替换原则:任何基类出现的地方,子类一定可以出现
45.单例模式的实现方式?
懒汉,饿汉,内部类,枚举,双重校验所DCL,compareAndSet(AutoReference)
45.创建工程的架构?
MVC架构: 也叫三层架构
DDD架构: 四层架构
六边形架构:
46.netty的优势?
使用简单:封装了nio的很多细节,使用起来更简单
性能好:比其他nio框架性能好
功能强大:之前多种编码解码方式
稳定性好:修复了很多nio的bug,让开发只关注业务本身
47.netty的高性能在于?
多线程Reactor反应器模式
对象池设计: 对象复用,减少创建对象的开销
内存零拷贝: socket ->直接内存 -> jvm内存 去除了到jvm内存的步骤
内存池设计: 因为netty堆外内存使用频率很高,池化管理安全
48.netty和tomcat的区别?
tomcat是web容器,netty是套接字服务
tomcat是http服务,netty可以实现多种服务http,ftp,udp,RPC
49.什么情况下需要分库分表?
单体应用承载了过多的业务需求,业务又增长加快
数据增量很大,数据库连接数扩容不能满足
50.高并发下提供给前端H5分页
select * from table where id > (select id from table order by id limit m, 1) limit n;
不能直接使用 limit x,y
51.项目运行较慢,重启后就好了
应用服务不足
io运算大
定时任务跑批
52.分库分表数据如何汇总查询
通过binlog日志同步到es,从es查询
53.什么是并发,什么是并行?
并发是合理的,高效的的控制共享资源
并行是如何利用更多的资源来产生高快速的响应
54.你觉的怎么编码更合理?
1.先跑起来在优化性能
2.小心冗长的参数列表
3.不要通过子类来扩展功能
4.使用设计模式来消除裸功能
网友评论