2020-09-15HTXX一面

一、Java基础

1、集合
image.png
  • HashMap底层实现
    数组+链表(JDK1.8引入红黑树,当链表长度超过8时,会将链表转为红黑树)
  • HashMap扩容
    当HashMap中的元素个数超过数组大小*负载因子时,会进行扩容,负载因子默认为0.75。也就是说,默认情况下,数组大小为16,当HashMap中的元素个数超过16*0.75=12时,就会进行扩容,把数组大小扩展为16*2,即原来的2倍。
  • 红黑树性质
    (1)每个节点要么是红色,要么是黑色;
    (2)根节点必须是黑色;
    (3)红色节点不能连续;
    (4)对于每个节点,从该点至叶子节点的任何路径,都含有相同个数的黑色节点;
    在插入和删除时,往往会破坏上述条件(3)和(4),需要通过调整使得树重新满足红黑树的条件。调整可以分为两类:一类是颜色调整,即改变某个节点的颜色;另一类是结构调整,即改变树的结构,结构调整包含两个基本操作:左旋和右旋。
  • TreeMap
    (1)TreeMap底层通过红黑树实现,这意味这containsKey(),get(),put(),remove()都有着log(n)的时间复杂度。
    (2)TreeMap是非同步的,可以通过SortedMap m= Collections.synchronizedSortedMap(new TreeMap<>());包装成同步的。
    (3)TreeMap实现了SortedMap接口,也就是说会按照key的大小顺序对Map中的元素进行排序,key大小的评判可以通过其本身的自然顺序,也可以通过构造时传入的比较器。
  • HashSet
    HashSet底层是HashMap
  • ArrayList和LinkedList的区别
    (1)ArrayList的底层是数组,LinkedList底层是双向链表;
    (2)对于随机访问,ArrayList优于LinkedList;
    (3)对于插入和删除操作,LinkedList优于ArrayList;
    (4)LinkedList比ArrayList更占内存,因为LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
2、线程池
image.png
  • Executor,ExecutorService以及Executors的区别
    (1)Executor和ExecutorService是接口,Executors是类;
    (2)ExecutorService接口继承了Executor接口,是Executor的子接口;
    (3)Executor接口定义了execute()方法用来接收一个实现 了runnable接口的对象,而ExecutorService接口中的submit()方法可以接收实现了Runnable和Callable接口的对象;Executor中的execute的方法不返回任何结果,ExecutorService中的submit方法可以通过一个future对象返回运算结果。
    (4)ExecutorService还提供了用来控制线程池的方法,比如调用shutdown方法终止线程。
    (5)Excutors类提供工厂方法用来创建不同类型的线程池,返回类型为ExecutorService。Executors提供四种线程池:newCachedThreadPool,创建一个可缓存线程池,如果线程池长度超过需要,可灵活回收空闲线程,若无可回收,则创建新线程;newFixedThreadPool,创建一个定长线程池,可控制线程的最大并发数,超出的线程会在队列中等待;newScheduledThreadPool,创建一个定长线程池,支持周期性任务执行;newSingleThreadExecutor,创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。
  • 线程池生命周期
    绘图1.png

    RUNNING:接收新的任务并处理队列中的任务;
    SHUTDOWN:不接收新任务,但是处理队列中的任务;
    STOP:不接收新任务,不处理队列中的任务,同时中断处理中的任务;
    TIDYING:所有的任务处理完成,有效线程数是0;
    TERMINATED:terminated()方法执行完毕。
  • 自定义线程池
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler);

corePoolSize:核心线程数量。
maximumPoolSize:最大线程数量。
keepAliveTime:空闲线程存活时间。当一个线程处于空闲状态,且当前线程数量大于核心线程数量,那么在keepAliveTime时间后,这个线程将被销毁。
unit:线程存活时间单位。
workQueue:等待队列。ArrayBlockingQueue:基于数组的有界阻塞队列,按FIFO排序;LinkedBlockingQueue:基于链表的无界阻塞

  • 线程池工作流程
    绘图6.png
  • 创建线程的三种方式
    (1)继承Thread类创建线程:a.继承Thread类并重写run方法;b.创建线程对象;c.调用该线程对象的start()方法来启动线程。
class ThreadTest extends Thread{
  @Override
  public void run(){
    //TODO:
  }
};

new ThreadTest().start();

(2)实现Runnable接口创建线程:a.定义一个类实现Runnable接口,并重写该接口的run()方法;b.创建Runnable实现类的对象,作为创建Thread对象的target参数,此Thread对象才是真正的线程对象;c.调用线程对象的start()方法来启动线程。

class RunnableTest implements Runnable{
  @Override
  public void run(){
    //TODO:
  }
}

RunnableTest runnableTest=new RunnableTest();
new Thread(runnableTest,"线程1").start();

(3)使用Callable和Future创建线程:a.定义一个类实现Callable接口,并重写call()方法,该call()方法将作为线程的执行体,并且有返回值;b.创建Callable实现类的实例,使用FutureTask类来包装Callable对象;c.使用FutureTask对象作为Thread对象的target创建并启动线程;d.调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

class CallableTest implements Callable{
  @Override
  public Integer call() throws Exception{
    //TODO:
  }
}

CallableTest callableTest=new CallableTest();
FutureTask<Integer> futureTask=new FutureTask<>(callableTest);
new Thread(futureTask).start();
futureTask.get();
3、JVM
  • JVM内存结构
    JVM内存结构可分为:方法区、堆、栈、本地方法栈、程序计数器。方法区和堆是所有线程共享的,栈、本地方法栈和程序计数器是线程私有的。
    image.png


    几乎所有的对象实例都是在堆上分配内存,分为新生代和老年代,新生代又分为Eden区,from区和to区。
    方法区
    用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

    虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会创建一个栈帧用于存储局部变量表、操作栈、动态链接、方法出口等信息。每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
    本地方法栈
    本地方法栈和虚拟机栈的作用类似,其区别在于虚拟机栈是为虚拟机执行Java方法服务,而本地方法栈则是为使用到的Native方法服务。
    程序计数器
    程序计数器是一个记录着当前线程所执行的字节码的行号指示器。
  • 如何判断对象是否存活
    引用计数法
    给对象添加一个引用计数器,每当有一个地方引用它时,计数器的值就加1,如果引用失效,计数器的值减1。当计数器的值为0时,表示该对象可以被回收。但存在两个对象相互循环引用的问题。
    可达性分析算法
    将“GC ROOTS”作为起始点,从这些GC ROOTS开始向下搜索,当GC ROOTS到这个对象不可达时,说明这个对象已经不再使用了,可以被回收。
  • 垃圾回收算法
    标记-清理算法
    首先标记出需要清理的对象,然后进行清理。存在内存碎片的问题。
    标记-整理算法
    首先标记出需要清理的对象,然后清除的时候会把所有存活的对象向一端移动,然后清除掉端边界以外的内存。
    复制算法
    把内存分为两部分,每次只使用其中的一部分,当这一部分的内存空间用完了,就把这部分还存活的对象复制到另一部分内存中,然后把之前那一部分的内存空间一次性清理掉。
  • 垃圾回收器
    CMS垃圾回收器
    a. 初次标记:标记GC ROOTS直接引用的对象
    b. 并发标记:标记所有old对象,不会STW
    c. 重新标记:修正第二步的并发标记,会STW
    d. 并发清理:采用标记-清理算法,不会STW
    G1垃圾回收器
    a. 初次标记:标记 GC ROOTS直接引用的对象,会STW
    b. 并发标记:扫描old区,标记不可达对象,不会STW
    c. 重新标记:修正第二步的并发标记,会STW
    d. 标记整理:对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容