面经问题整理

来源:互联网 发布:淘宝网外贸店 编辑:程序博客网 时间:2024/06/05 07:46

面经问题整理

一、HashMap如何实现的?

jdk1.7以前,hashMap的数据结构是数组+链表的形式,hashMap中维护着一个Entry[]数组变量,Entry中维护着key,value,hash,next(下一个Entry)。
当向hashMap对象put一个键值对时,首先会先根据key的hashCode值计算出一个值(hashCode值与数组的长度取模),即数组的索引。如果该位置没有对象,则直接将此对象放进数组当中;如果该位置已经存在对象,则顺着该数组的链表一个一个遍历下去,如果根据key的equal方法没有匹配到,则放进链表的第一位,以前的对象链接到此对象之后;如果key的equal方法匹配了,那么将覆盖原有的value值。
hashMap的get方法也类似,也是根据hashCode去找到数组的位置,然后根据equal方法去匹配key值。
如果hash碰撞严重(即链表的长度很长),那么jdk1.7的性能会很差,因为每次put和get都要遍历整个链表去判断key值是否存在,最差的情况时间复杂度为O(n),所以在jdk1.8提出了红黑树的方式实现hashMap,当链表的长度超过阀值(8)时,链表转成了红黑树。

二、RPC的实现原理

RPC:远程过程调用,允许一台计算机调用例外一台计算机的程序得到结果,就像调用本地方法一样。比如两台服务器A,B,A服务器上想要调用B服务器上的接口方法,由于不再同一个内存空间,不能直接调用,这时候可以用RPC框架的实现来解决。
RPC框架需要一个server端,一个client端和register注册中心。
使用到的技术:1、动态代理,生成 client stub和server stub需要用到 Java 动态代理技术。2、序列化,client端和server端方法的参数通过底层的网络协议进行传输的,由于网络协议是基于二进制的,所以需要进行序列化和反序列化。3、通信,数据序列化为二进制后,需要进行网络通信,很多RPC框架是基于Netty这个通信框架。

三、 线程池

为什么要用线程池?频繁的创建和销毁线程开销大,大大降低了系统的效率;线程间的频繁上下文切换可能导致cup繁忙,最终导致内存溢出。
Executors提供四种线程池:
1、newFixedThreadPool 创建一个固定长度的线程池,可控制线程的最大并发数,超过定长线程会在LinkedBlockingQueue队列等待;

//Executors的静态方法,底层实际是实例了一个ThreadPoolExecutor对象,空线程会一直保留,请求没有空线程自动存入LinkedBlockingQueue队列等待public static ExecutorService newFixedThreadPool(int nThreads){      return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());  } 

2、newCachedThreadPool 创建一个可重用线程池,缓存时间默认60s,长度为Integer.MAX_VALUE,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

//Executors的静态方法,底层实际是实例了一个ThreadPoolExecutor对象,空线程保留60s后会自动销毁;SynchronousQueue是一个同步阻塞队列,size为0,put的数据需要take后才能put其他数据public static ExecutorService newCachedThreadPool() {            return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());    }

3、newSingleThreadPool 创建了一个单线程化线程池,只有一个线程可执行任务,保证所有任务按指定的顺序执行。

//Executors的静态方法,底层实际是实例了一个ThreadPoolExecutor对象,只有一个线程的线程池 public static ExecutorService newSingleThreadExecutor() {      return new FinalizableDelegatedExecutorService          (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));  }  

4、newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

//Executors的静态方法,底层实际是实例了一个ScheduledThreadPoolExecutor对象,预定执行而构建的固定线程池public static ScheduledExecutorService      newScheduledThreadPool(int corePoolSize) {          return new ScheduledThreadPoolExecutor(corePoolSize);  }

四、 为什么用RPC,而不用简单的HTTP接口

论复杂,其实rpc框架的复杂肯定高于简单的用现有的httpClient调用请求。但毋庸置疑,http接口受限于http协议,每次请求需要三次握手,需要带http请求头,传输效率和安全性都不如rpc。
1、当需要调用其他服务的接口很多时,rpc比http减少了网络开销;
2、rpc框架一般有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来说无感知;
3、安全性、性能高效。
一般对外开放的接口都是提供restful的http接口,而内部调用可以看情况的使用rpc框架。

五、JVM内存分为哪几个区,主要功能

1、程序计数器:一块较小的内存空间,线程私有的,指向正在执行的字节码指令的地址;
2、Java虚拟机栈:也是线程私有的,生命周期和线程相同。每个线程都有一个独有的栈,在执行每个方法的时候会创建一个栈帧,入栈后会在栈顶,栈帧用于存放局部变量表(基本数据类型、对象的引用)、操作栈、方法出口、动态链接等信息,等待方法执行结束就会出栈。
3、本地方法栈:与Java虚拟机栈功能相似,区别是本地方法栈是为虚拟机的Native方法服务。
4、Java堆:内存中最大的一块,是线程共享的区域。主要存放的是对象实例和数组。Java堆也是垃圾收集器管理的主要区域。
5、方法区:也是线程共享的区域,用于存放已被虚拟机加载的类信息、常量、静态变量、即时编译期编译后的代码(类方法)等数据。

六、Java虚拟机类的加载机制

类的生命周期包括加载、验证、准备、解析、初始化、使用、卸载七个阶段。其中类的加载机制包括前面五个阶段。
1、加载:1.1、根据类的全限定名找到二进制文件;1.2、讲字节流的代表的静态存储结构转化为虚拟机方法区的数据结构;1.3、在Java堆中生成对应的实例对象,作为对方法区数据访问的入口。
2、验证:主要是验证二进制文件所存储的数据是否符合虚拟机的规范。
3、准备:静态变量初始化,常量被赋正确的值。
4、解析:将符号引用转换为直接引用。类或接口的解析、字段解析、类方法的解析、接口方法的解析。
5、初始化:真正执行Java代码的阶段,在准备阶段初始化的静态变量需要赋予真正的值。执行类的构造器(()),子类调用构造器方法需要先执行父类的。

七、JMM(Java内存模型)

Java内存模型中规定所有变量都必须存放在主内存中,每个线程中有自己独有的工作内存,工作内存中的变量是主内存的副本拷贝,每个线程不能操作主内存的变量,只能操作自己工作内存中的变量。所以,在这个过程会引发数据一致性的问题。所以提出了JMM的三个特性:
1、原子性:一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。一般需要用lock、synchronized等来保证。
2、可见性:当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。主要用volidate修饰变量。
3、有序性:如果在本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。

concurrent包,你用过哪些?

1、Semaphore信号量,定义一个大小的信号量集,每个线程进入前先调用Semaphore的acquire方法获取许可,获取到许可的线程可以继续执行,并将可用许可减一;未获取到许可的线程自旋阻塞等待其他线程release释放许可;
2、Atomic原子类,主要用AtomicInteger,多线程操作同一个变量,利用其的原子性,调用CAS方法操作共享变量;
3、ReetrantLock重入锁,重入锁中有个Sync类,继承了AQS类,内部还有公平和非公平的类,继承了Sync类;有一个volidate变量state,根据state的状态确认线程是否获取到锁,如果是当前线程多次获取锁,state会大于1;如果获取锁失败,则需要将线程加入CLH队列尾部,然后通过自旋继续等待获取锁。
4、CountDownLatch一个辅助计数器,可以让一个或多个线程等待需要先执行完结果的线程。用countdown方法减引用,用await方法等待。
5、BlockingQueue阻塞队列,主要用到ArrayBlockingQueue、LinkedBlockingQueue,一般和线程池一起使用,让线程池的线程poll队列数据,等到poll数据为空,则将状态置false结束线程。

spring的自定义注解

1、@Target
2、@Retention
3、@Documented
4、@Inherited