一、Java基础
1、集合
-
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、线程池
-
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,优先级)执行。 -
线程池生命周期
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:基于链表的无界阻塞
-
线程池工作流程
-
创建线程的三种方式
(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内存结构可分为:方法区、堆、栈、本地方法栈、程序计数器。方法区和堆是所有线程共享的,栈、本地方法栈和程序计数器是线程私有的。
堆
几乎所有的对象实例都是在堆上分配内存,分为新生代和老年代,新生代又分为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停顿时间来制定回收计划。