近間陸續面試了很多的求職的前(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爲什麼不須要自動釋放或關閉;