记录一些搜集的面试题,方便熟悉八股文~~
- 类的初始化顺序
父类静态变量(静态代码块,只初始化一次,保存在jvm的内存的方法区中) -> 子类静态变量(静态代码块,只初始化一次,保存在jvm的方法区中)-> 父类非静态变量(非静态代码块,实例化后保存在jvm的堆中)-> 父类构造方法 ->子类非静态变量(非静态代码块,可多次是实例化,实例化后保存在jvm的堆中)-> 子类构造方法
- java hashmap
数据结构底层:数组-方便通过Index快速定位,链表-解决hash冲突,方便插入和删除,红黑树-解决连边查询较慢问题。
单个索引长度大于8且数组长度大于64,表示数据达到一定量之后,链表转为红黑树,这个应该实验依据得来的。否则就扩容。
扩容为什么是2的幂次方,初始化时16?java jdk8 hash算法?
为了减少hash的碰撞率,在object hash之后,java还有通过一次hash来减少碰撞率,具体做法是利用原hash值得高16位异或低16位,然后&上数组的容量-1。所有得是2的幂次方
concurrenHashmap
jdk1.7: 底层分段数组+链表,分段所segment(继承reentrantlock),对数组分段(多个Node)锁,提升性能
jdk1.8: cas(内存屏障,立即刷新朱内存)+synchronized,锁住每个数组头节点,颗粒度更低,降低所冲突概率,提升并发性能
(hashtable一个实例一把锁,相当于 串行,效率低)
concurrenHashmap扩容
计算每个线程可以处理的桶区间。默认 16.
初始化临时变量 nextTable,扩容 2 倍。
死循环,计算下标。细节比较繁琐,总结起来就是每个线程处理不同的数组区间
- 线程池
作用:节约系统创建开支,复用线程,管理以及监控线程
线程池核心属性: workqueue-线程池队列 keepaliveTime -非核心线程存活时间 Handler -异常处理器 corepollSize -核心线程数 maximumPoolSize -最大线程数 threadFactory -线程工厂创建新线程
主要工作流程:
(1)当阻塞队列已经和核心满了的时候,会开启新的工作线程。当达到最大的工作线程,会走拒绝策略。
(2)当阻塞队列(多并发的,需要阻塞,阻塞原理为retreenLock)未满 ,会加入队列,然后线程去队列取任务,未取到任务时,会销毁线程,表示其他线程已经执行。
线程池的五个状态: RUNNING接受新任务,SHUTDOWN:不接受新任务,但处理任务
STOP:中断任务,不处理排队任务; DYING:所有任务已停止,将运行terminated钩子方法。TERMINATED: terminater钩子方法已经执行。
常见的阻塞队列,ArrayBlockingQueue,基于数组的有界队列,先进先出对元素进行排序
LinkedBlockingQueue, 链表的无界队列,newFixedTheadpool使用了它。
synchronousQueue 相当于一个立即阻塞的队列,收到任务立马发给线程池执行,若线程池线程满了或者,提交的任务将会被拒绝,适合需要响应快的任务。
PriorityBlockingQueue 具有优先级任务的队列
无界队列的任务需要考虑内存溢出的情况,有界队列需要考虑是否如何拒绝策略。
AbsortPolicy 中止策略,跑出RejectedExecutionException。调用者可以捕获异常,然后自行处理。
DiscardPolicy 抛弃策略,直接配抛弃,什么都不做
DiscardOldestPolicy: 抛弃最老的任务
CallerRunsPolicy: 将任务回退到调用者
prestartCoreThread可以提前启动核心线程
核心线程和非核心线程?
核心线程通过阻塞队列保持一直存活,非核心线程通过poll(keepAliveTime)来保证存活时间。记得调用shutdown缓慢关闭线程池,SHUTDOWN状态到dying状态,销毁线程池
Executors几种创建的线程池方法
newFixedThreadPool n个核心线程,非核心0秒的存活时间,表示没有任务立马销毁,无界阻塞队列,以及自定义线程生产工厂,适用于控制定量线程工作,负载较重的服务器。ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory);
newSingleThreadScheduledExecutor 1个核心线程,最大的非核心线程,非核心0秒的存活时间,表示没有任务立马销毁,延迟队列,同步线程,表示任务将会一个一个执行。
newCachedThreadPool 0个核心线程,最大的非核心线程,非核心60秒的存活时间,表示,延迟队列,同步队列,里面提交给线程池,适合于快速响应的,负载较小的功能。super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
线程中涉及的Ctl作用,ctl前三位是表示线程池状态,后29位表示线程池线程个数,将两者合一可以避免对两者加锁来获取或者更改值,直接使用原子变量表示就行。具体获取通过取反和按位与操作。new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
线程池参数设计,主要考虑线程的是io密集操作还是计算密集,如果是io密集型,可增大线程池线程个数,让cpu得到更充分利用。如果是计算密集,正常来讲使用cpu数+1就行 - cas操作
countDownLatch原理感觉和thread.yeild()以及thread.activeCount原理类似
原理:循环读取内存的值,利用compareandset底层方法。比较赋值,再多处理器总,该方法会有 锁缓存,其他处理器不能读写,然后强制刷新缓存到内存,保证操作的原子性以及可见性
优缺点,保证一个共享变量原子操作,循环耗费时间,ABA问题
- mysql
mysql的事务隔离级别?解决问题?
读未提交(read uncommitte)读到的是最新数据 存在脏读、不可重复读、幻读
读已提交 (read committed ) 解决脏读(猜测:利用隐藏的事务版本号) 存在不可重复读和幻读
可重复读(raed rereated) 使用mvcc解决,多版本并发控制,行引用了未提交事务快照,感觉流程很麻烦。也是利用版本号来控制。解决了脏读和不可重复读,未解决幻读。
还有一个串行化,无上述问题,性能低
Mysql默认是rr级别,因为Mysql5.6在rc级别下有主从复制Bug,两个事物,先插后删。但是Binlog会根据事物顺序记录成先删后插,导致数据不一致。备机回放就会出现问题。
https://zhuanlan.zhihu.com/p/59061106
幻读如何解决的?
幻读是一个事务读取了另一个事务插入的新行。
首先区分下快照读和当前读,快照读是生成一个事务快照,普通select从这个快照读取。
当前读是update/delete/insert select for 等,读取最新版本。
mvvc就是快照读,所以可以解决select的幻读。
对于当前读,是通过gap锁来解决,锁住区间,其他操作无法插入数据,来解决幻读。
另外,Mysql的rr级别默认开启gap锁,锁住行区间
互联网项目为啥采用rc,提交读
RR隔离级别下,存在间隙锁,导致出现死锁的几率比RC大的多!
在RR隔离级别下,条件列未命中索引会锁表!而在RC隔离级别下,只锁行 此时执行语句
在RC隔离级别下,半一致性读(semi-consistent)特性增加了update操作的并发性!
rc下可重复读的问题,数据都已经提交了,读出来本身就没有太大问题!