近间陆续面试了很多的求职的前(JAVA)、后(WEB)端开发人员,包括实习生、应届毕业生、一两年工做经验的、也有三四年工做经验的,也算见过了比较多的开发人员,想在这里作个总结,本次主要讲一讲面试和后端(java)相关的东西;html
a、HashMap是非线程安全的,HashTable是线程安全的。前端
b、HashMap的键和值都容许有null值存在,而HashTable则不行。java
c、由于线程安全的问题,HashMap效率比HashTable的要高。面试
一、B+树redis
参考:B+树介绍算法
二、八大排序算法sql
参考:八大排序算法JAVA实现数据库
一、JVM的内存结构编程
答:主要分为三大块 堆内存、方法区、栈;栈又分为JVM栈、本地方法栈segmentfault
堆(heap space),堆内存是JVM中最大的一块,有年轻代和老年代组成,而年轻代又分为三分部分,Eden区,From Survivor,To Survivor,默认状况下按照8:1:1来分配
方法区(Method area),存储类信息、常量、静态变量等数据,是线程共享的区域
程序计数器(Program counter Register),是一块较小的内存空间,是当前线程所执行的字节码的行号指示器
JVM栈(JVM stacks),也是线程私有的,生命周期与线程相同,每一个方法被执行时都会建立一个栈帧,用于存储局部变量表、操做栈、动态连接、方法出口等信息
本地方法栈(Native Mthod Stacks),为虚拟机使用的native方法服务
二、关于垃圾回收和常见的GC算法,请参考:GC专家系列-理解java垃圾回收
一、JAVA实现多线程的几种方式
a、继承Thread类实现
public class MyThread extends Thread { public void run() { System.out.println("MyThread.run()"); } } MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.start(); myThread2.start();
b、实现Runnable接口
public class MyThread extends OtherClass implements Runnable { public void run() { System.out.println("MyThread.run()"); } } MyThread myThread = new MyThread(); Thread thread = new Thread(myThread); thread.start();
c、使用ExecutorService、Callable、Future实现有返回结果的多线程
import java.util.concurrent.*; import java.util.Date; import java.util.List; import java.util.ArrayList; /** * 有返回值的线程 */ @SuppressWarnings("unchecked") public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("----程序开始运行----"); Date date1 = new Date(); int taskSize = 5; // 建立一个线程池 ExecutorService pool = Executors.newFixedThreadPool(taskSize); // 建立多个有返回值的任务 List<Future> list = new ArrayList<Future>(); for (int i = 0; i < taskSize; i++) { Callable c = new MyCallable(i + " "); // 执行任务并获取Future对象 Future f = pool.submit(c); // System.out.println(">>>" + f.get().toString()); list.add(f); } // 关闭线程池 pool.shutdown(); // 获取全部并发任务的运行结果 for (Future f : list) { // 从Future对象上获取任务的返回值,并输出到控制台 System.out.println(">>>" + f.get().toString()); } Date date2 = new Date(); System.out.println("----程序结束运行----,程序运行时间【" + (date2.getTime() - date1.getTime()) + "毫秒】"); } } class MyCallable implements Callable<Object> { private String taskNum; MyCallable(String taskNum) { this.taskNum = taskNum; } public Object call() throws Exception { System.out.println(">>>" + taskNum + "任务启动"); Date dateTmp1 = new Date(); Thread.sleep(1000); Date dateTmp2 = new Date(); long time = dateTmp2.getTime() - dateTmp1.getTime(); System.out.println(">>>" + taskNum + "任务终止"); return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】"; } }
二、Callable和Future
答:Callable接口相似于Runnable,可是Runnable不会返回结果,而且没法抛出返回结果的异常,而Callable更强大,被线程执行之后,能够返回值,这个返回值就是经过Future拿到,也就是说,Future能够拿到异步执行任务的返回值,能够看如下例子:
import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class Test { public static void main(String[] args) { Callable<Integer> callable = new Callable<Integer>() { @Override public Integer call() throws Exception { return new Random().nextInt(100); } }; FutureTask<Integer> futureTask = new FutureTask<Integer>(callable); new Thread(futureTask).start(); try { Thread.sleep(1000); System.err.println(futureTask.get()); } catch (Exception e) { e.printStackTrace(); } } }
ExecutorService继承自Executor,目的是为咱们管理Thread对象,从而简化并发变成,Executor使咱们无需显示的去管理线程的声明周期,是JDK5以后启动任务的首选方式。
执行多个带返回值的任务,并取得多个返回值,代码以下:
import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CallableAndFuture { public static void main(String[] args) { ExecutorService threadPool = Executors.newCachedThreadPool(); CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool); for( int i = 0; i < 5; i++ ){ final int taskId = i; cs.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { return taskId; } }); } for( int i = 0; i < 5; i++ ){ try { System.err.println(cs.take().get()); } catch (Exception e) { e.printStackTrace(); } } } }
三、线程池的参数有哪些,在线程池建立一个线程的过程
corePoolSize:核心线程数,可以同时执行的任务数量
maximumPoolSize:除去缓冲队列中等待的任务,最大能容纳的任务数(其实就是包括了核心线程池的数量)
keepAliveTime:超出workQueue的等待任务的存活时间,就是指maximumPoolSize里面的等待任务的存活等待时间
unit:时间单位
workQueue:阻塞等待线程的队列,通常使用new LinkedBlockingQueue()这个,若是不指定容量,会一直往里添加,没有限制,workQueue永远不会满,通常选择没有容量上限的队列
threadFactory:建立线程的工厂,使用系统默认的类
handler:当任务数超过maximumPoolSize时,对任务的处理策略,默认策略是拒绝添加
执行流程:当线程数小于corePoolSize时,每添加一个任务,则当即开启线程执行;当corePoolSize满的时候,后面添加的任务将放入缓冲队列workQueue等待;当workQueue满的时候,看是否超过maximumPoolSize线程数,若是超过,则拒绝执行,若是没有超过,则建立线程理解执行;
1 import java.util.concurrent.Executors; 2 import java.util.concurrent.LinkedBlockingQueue; 3 import java.util.concurrent.ThreadPoolExecutor; 4 import java.util.concurrent.TimeUnit; 5 6 /** 7 * 对线程池进行管理和封装 8 * @author guoqing 9 * 10 */ 11 public class ThreadPoolManager { 12 13 private static ThreadPoolManager mInstance = new ThreadPoolManager(); 14 private ThreadPoolExecutor executor; 15 16 private int corePoolSize; //核心线程池数量,表示可以同时执行的任务数量 17 private int maximumPoolSize; //最大线程池数量,实际上是包含了核心线程池数量在内的 18 private long keepAliveTime = 1; //存活时间,表示最大线程池中等待任务的存活时间 19 private TimeUnit unit = TimeUnit.HOURS; //存活时间的时间单位 20 21 public static ThreadPoolManager getInstance() { 22 return mInstance; 23 } 24 25 private ThreadPoolManager() { 26 //核心线程数量的计算规则:当前设备的可用处理器核心数*2+1,可以让cpu获得最大效率的发挥 27 corePoolSize = Runtime.getRuntime().availableProcessors()*2+1; 28 maximumPoolSize = corePoolSize; //虽然用不到,可是不能为0,不然会报错 29 //线程池机制:领工资的机制 30 executor = new ThreadPoolExecutor(corePoolSize, 31 maximumPoolSize, 32 keepAliveTime, 33 unit, 34 new LinkedBlockingQueue<Runnable>(), //缓冲队列,超出核心线程池的任务会被放入缓冲队列中等待 35 Executors.defaultThreadFactory(), //建立线程的工厂类 36 new ThreadPoolExecutor.AbortPolicy() //当最大线程池也超出的时候,则拒绝执行 37 ); 38 } 39 40 /** 41 * 往线程池中添加任务 42 * @param r 43 */ 44 public void executor(Runnable r) { 45 if(r!=null) { 46 executor.execute(r); 47 } 48 } 49 50 /** 51 * 从线程池中移除任务 52 * @param r 53 */ 54 public void remove(Runnable r) { 55 if(r!=null) { 56 executor.remove(r); 57 } 58 } 59 }
四、volatile关键字的做用,原理
答:保证内存可见性和禁止指令重排。实现原理可参考:JAVA并发变成--valatile关键字剖析
五、synchronized关键字的用法,优缺点
答:java关键字,当它用来修饰一个方法或者代码块的时候,可以保证在同一时刻最多只有一个线程执行该代码段的代码;
synchronized修饰的方法或者对象,只能以同步的方式执行,会引发性能问题;没法中断一个正在等候得到锁的线程,也没法经过投票得到锁;一个优先级高的线程等待一个优先级低的线程释放锁会致使优先级倒置,引发性能风险;
六、Lock接口有哪些实现类,使用场景是什么
答:Lock接口有三个实现类,一个是ReentrantLock,另两个是ReentrantReadWriteLock类中的两个静态内部类ReadLock和WriteLock。
使用场景:通常应用于多度少写,由于读的线程之间没有竞争,因此比起synchronzied,性能要好不少;
十、sleep和wait的区别
答:首先,sleep()方法属于Thread类的,而wait()方法是属于Object类的;sleep()方法致使了程序暂停执行指定的时间,让出cpu给其余线程,可是他的监控状态依然保持,当指定的时间到了又自动回恢复运行状态,调用了sleep()方法的过程当中,线程不会释放对象锁;而当调用了wait()方法的时候,线程回放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备。
一、常见的数据库优化手段
答:库表优化,表设计合理化,符合三大范式;添加适当的索引(普通索引、主键索引、惟一索引、全文索引);分库分表;读写分离等;sql语句优化,定位执行效率低,慢sql的语句,经过explain分析低效率的缘由;
二、索引的优缺点,什么字段上创建索引
答:优势方面:第一,经过建立惟一索引能够保证数据的惟一性;第二,能够大大加快数据的检索速度,是主要目的;第三;在使用分组和排序子句进行数据检索时,能够显著减小查询中分组和排序的时间;第四,能够在查询中使用优化隐藏器,提升系统的性能;
缺点方面:第一,建立索引和维护索引要耗费时间,而且随着数据量的增长而增长;第二,每个索引须要占用额外的物理空间,须要的磁盘开销更大;第三,当对表中的数据进行增长、删除、修改操做时,索引也要动态维护,下降了数据的维护速度;
一、TCP和UDP的区别
答:TCP(传输控制协议),UDP(用户数据报协议)
(1)TCP面向链接(如打电话先拨号创建链接);UDP是无链接的,即发送数据以前不须要创建链接;
(2)TCP提供可靠的服务。也就是说,经过TCP链接传送的数据,无差错,不丢失,不重复,且按序达到;UDP尽最大努力交付,即不保证可靠交付;
(3)TCP面向字节流,其实是TCP把数据当作一连串无结构的字节流;UDP是面向报文,UDP没有拥塞控制,所以网络出现拥塞不会使源主机的发送速率下降(对实时应用颇有用,如IP电话,实时视频会议等)
(4)每一条TCP链接只能是点到点的,UDP支持一对一,一对多,多对一和多对多的交互通讯;
(5)TCP首部开销20字节,UDP首部开销8字节;
(6)TCP的逻辑通讯信道是全双工的可靠信道,DUP则是不可靠信道;
四次挥手:
A:“喂,我不说了 (FIN)。”A->FIN_WAIT1
B:“我知道了(ACK)。等下,上一句还没说完。Balabala…..(传输数据)”B->CLOSE_WAIT | A->FIN_WAIT2
B:”好了,说完了,我也不说了(FIN)。”B->LAST_ACK
A:”我知道了(ACK)。”A->TIME_WAIT | B->CLOSED
A等待2MSL,保证B收到了消息,不然重说一次”我知道了”,A->CLOSED
三、长链接和短链接。
短链接:链接=》传输数据=》关闭链接
HTTP是无状态的,浏览器和服务器之间每进行一次http操做,就创建一次链接,但任务结束就中断链接;也能够理解为短链接是指socket链接后,发送接收完数据立刻断开链接;
长链接:链接=》传输数据=》保持链接=》传输数据=》。。。=》关闭链接
长链接指创建socket链接后无论是否使用都保持链接,但安全性较差;
此处推荐阅读:java23种设计模式 深刻理解
一、单例模式的几种写法
懒汉模式
public class Singleton { private static Singleton instance = null; private Singleton(){} public static synchronized Singleton getInstance(){ //若是尚未被实例化过,就实例化一个,而后返回 if(instance == null){ instance = new Singleton(); } return instance; } }
饿汉模式
public class Singleton { //类加载的时候instance就已经指向了一个实例 private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }
双重检验锁
public class Singleton { private static Singleton instance = null; private Singleton(){} public static Singleton getInstance(){ if(instance == null){ synchronized (Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; } }
静态内部类:由于JAVA静态内部类的特性,加载的时候不会加载内部静态类,使用的时候才会加载,而使用的时候类加载又是线程安全的,这就完美达到了效果;
public class Singleton { private static class SingletonHolder{ private static Singleton instance = new Singleton(); } private Singleton(){} public static Singleton getInstance(){ return SingletonHolder.instance; } }
枚举:
public enum Singleton { INSTANCE; }
二、Spring使用了哪些设计模式
(1)工厂模式,在各类BeanFactory以及ApplicationContext建立中都用到了;
(2)模板模式,也是在各类BeanFactory以及ApplicationContext建立中都用到了;
(3)代理模式,在AOP实现中用到了JDK的动态代理;
(4)单例模式,好比建立bean的时候;
(5)策略模式,第一个地方,加载资源文件的地方,使用了不一样的方法,好比:classPathResource,FileSystemResource,ServletContextResource,UrlResource但他们都有共同的接口Resource;第二个地方就是AOP的实现中,采用了不一样的方式,JDK动态代理和CGLIB代理;
一、分布式事务的控制
能够参考分布式系统事务一致性解决方案
二、分布式锁
答:通常使用zk瞬时有序节点实现的分布式锁,或者利用redis的setnx()封装分布式锁;提供思路,具体的能够自行详细理解;
三、分布式session如何设计
答:一个比较成熟的方案是经过redis进行session共享。详细的原理能够参考一种分布式session实现方案
四、关于dubbo
能够参考博文:Dubbo学习总结(2)——Dubbo架构详解
五、能够了解zk相关知识
一、redis和memcached的区别
(1)redis和memcache都是将数据放入内存中,都是内存数据库。可是memcache能够缓存图片、视频等数据;
(2)redis不只仅支持简单的k/v数据,还提供list、set、hash等数据结构的存储;
(3)虚拟内存--redis当物理内存用完时,能够将一些好久没有用到的value交换到磁盘;
(4)过时策略--memcache在set时就指定,例如set key1008,即永不过时,redis经过expire设定;
(5)分布式--设定memcache集群,利用magent作一主多从;redis能够作一主多从或一主一从;
(6)存储数据安全--memcache挂掉后,数据没了,redis能够按期保存到磁盘进行持久化;
(7)灾难恢复--memcache挂掉后,数据不可恢复。redis数据丢失后能够经过aof恢复;
(8)redis支持数据备份,即master-slave主备模式;
二、redis是单线程的么(是的)
三、redis的持久化策略
答:rdb:快照形式是直接把内存中的数据保存到一个dump文件中,定时保存
aof:把全部的对redis的服务器进行修改的命令都存到一个文件里,命令的集合
一、SpringMvc工做原理
(1)用户发送请求至前端控制器DispatcherServlet
(2)DispatcherServlet收到请求调用HandlerMapping处理映射器
(3)处理器映射器找到具体的处理器(能够根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(若有则生成)一并返回给DispatcherServlet
(4)DispatcherServlet调用HandlerAdapter处理器映射器
(5)HandlerAdapter通过适配调用具体的处理器(Controller,也叫后端控制器)
(6)Controller执行完成返回ModelAndView
(7)HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器
(9)ViewResolver解析后返回具体的view
(10)DispatcherServlet根据view进行试图渲染(即将模型数据填充至视图中)
(11)DispatcherServlet响应用户
如下组件一般使用框架提供实现:
DispatcherServlet:做为前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,下降组件之间的耦合性,提升每一个组件的扩展性。
HandlerMapping:经过扩展处理器映射器实现不一样的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
HandlAdapter:经过扩展处理器适配器,支持更多类型的处理器。
ViewResolver:经过扩展视图解析器,支持更多类型的视图解析,例如:jsp、freemarker、pdf、excel等。
二、Quartz概念及原理
org.quartz.Job:它是一个抽象接口,表示一个工做,也是咱们要执行的具体的内容,只定义了一个接口方法:void execute(JobExecutionContext context)
org.quartz.JobDetail:JobDetail表示一个具体的可执行的调度程序,Job是这个可执行调度程序所要执行的内容,它包含了这个调度任务的方案和策略
org.quartz.Trigger:Trigger是一个抽象接口,表示一个调度参数的配置,经过配置他,来告诉调度器何时去调用JobDetail
org.quartz.Scheduler:一个调度容器,能够注册多个Trigger和JobDetail。当Trigger和JobDetail组合,就能够被Scheduler容器调度了
三、Spring的IOC有什么优点
答:要了解IOC首先要明白依赖倒置原则(Dependency Inversion Principle),就是把本来的高层建筑依赖底层建筑倒置过来,变成底层建筑依赖高层建筑。高层建筑决定须要什么,底层去实现这样的需求,可是高层并不用管底层的是怎么实现的;而控制反转(Inversion of Control)就是依赖倒置原则的一种代码的设计思路;
IOC思想的核心,资源不禁使用资源的双方管理,由不适用资源的第三方管理。
优点:资源集中管理,实现资源的可配置和易管理;下降了使用资源双方的依赖程度,也就是下降了耦合度;
四、Mybatis的设计思想,以及动态代理的真正实现
Mybatis中的mapper没有实现类,只有对应的xml文件,是如何实现的;
Spring整合Mybatis时sqlsession为什么不须要自动释放或关闭;