记录一些搜集的面试题,方便熟悉八股文~~

  1. 类的初始化顺序
    父类静态变量(静态代码块,只初始化一次,保存在jvm的内存的方法区中) -> 子类静态变量(静态代码块,只初始化一次,保存在jvm的方法区中)-> 父类非静态变量(非静态代码块,实例化后保存在jvm的堆中)-> 父类构造方法 ->子类非静态变量(非静态代码块,可多次是实例化,实例化后保存在jvm的堆中)-> 子类构造方法
     
  2. 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 倍。
    死循环,计算下标。细节比较繁琐,总结起来就是每个线程处理不同的数组区间
  3. 线程池

    作用:节约系统创建开支,复用线程,管理以及监控线程

    线程池核心属性: 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秒的存活时间,表示没有任务立马销毁,延迟队列,同步线程,表示任务将会一个一个执行。

super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
  1.  newCachedThreadPool 0个核心线程,最大的非核心线程,非核心60秒的存活时间,表示,延迟队列,同步队列,里面提交给线程池,适合于快速响应的,负载较小的功能。
new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
  1. 线程中涉及的Ctl作用,ctl前三位是表示线程池状态,后29位表示线程池线程个数,将两者合一可以避免对两者加锁来获取或者更改值,直接使用原子变量表示就行。具体获取通过取反和按位与操作。

    线程池参数设计,主要考虑线程的是io密集操作还是计算密集,如果是io密集型,可增大线程池线程个数,让cpu得到更充分利用。如果是计算密集,正常来讲使用cpu数+1就行
  2. cas操作
    countDownLatch原理感觉和thread.yeild()以及thread.activeCount原理类似
    原理:循环读取内存的值,利用compareandset底层方法。比较赋值,再多处理器总,该方法会有 锁缓存,其他处理器不能读写,然后强制刷新缓存到内存,保证操作的原子性以及可见性
    优缺点,保证一个共享变量原子操作,循环耗费时间,ABA问题
     
  3. 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下可重复读的问题,
    数据都已经提交了,读出来本身就没有太大问题!