(最新)各大公司Java後端開發面試題總結

ThreadLocal(線程變量副本)css

 

Synchronized實現內存共享,ThreadLocal爲每一個線程維護一個本地變量。html

 

採用空間換時間,它用於線程間的數據隔離,爲每個使用該變量的線程提供一個副本,每一個線程均可以獨立地改變本身的副本,而不會和其餘線程的副本衝突。java

 

ThreadLocal類中維護一個Map,用於存儲每個線程的變量副本,Map中元素的鍵爲線程對象,而值爲對應線程的變量副本。web

 

ThreadLocal在Spring中發揮着巨大的做用,在管理Request做用域中的Bean、事務管理、任務調度、AOP等模塊都出現了它的身影。算法

 

Spring中絕大部分Bean均可以聲明成Singleton做用域,採用ThreadLocal進行封裝,所以有狀態的Bean就可以以singleton的方式在多線程中正常工做了。sql

 

Java內存模型:數據庫

 

Java虛擬機規範中將Java運行時數據分爲六種。vim

 

1.程序計數器:是一個數據結構,用於保存當前正常執行的程序的內存地址。Java虛擬機的多線程就是經過線程輪流切換並分配處理器時間來實現的,爲了線程切換後能恢復到正確的位置,每條線程都須要一個獨立的程序計數器,互不影響,該區域爲「線程私有」。跨域

 

2.Java虛擬機棧:線程私有的,與線程生命週期相同,用於存儲局部變量表,操做棧,方法返回值。局部變量表放着基本數據類型,還有對象的引用。數組

 

3.本地方法棧:跟虛擬機棧很像,不過它是爲虛擬機使用到的Native方法服務。

 

4.Java堆:全部線程共享的一塊內存區域,對象實例幾乎都在這分配內存。

 

5.方法區:各個線程共享的區域,儲存虛擬機加載的類信息,常量,靜態變量,編譯後的代碼。

 

6.運行時常量池:表明運行時每一個class文件中的常量表。包括幾種常量:編譯時的數字常量、方法或者域的引用。

 

「你能不能談談,java GC是在何時,對什麼東西,作了什麼事情?」

 

在何時:

1.新生代有一個Eden區和兩個survivor區,首先將對象放入Eden區,若是空間不足就向其中的一個survivor區上放,若是仍然放不下就會引起一次發生在新生代的minor GC,將存活的對象放入另外一個survivor區中,而後清空Eden和以前的那個survivor區的內存。在某次GC過程當中,若是發現仍然又放不下的對象,就將這些對象放入老年代內存裏去。

 

2.大對象以及長期存活的對象直接進入老年區。

 

3.當每次執行minor GC的時候應該對要晉升到老年代的對象進行分析,若是這些立刻要到老年區的老年對象的大小超過了老年區的剩餘大小,那麼執行一次Full GC以儘量地得到老年區的空間。

 

對什麼東西:從GC Roots搜索不到,並且通過一次標記清理以後仍沒有復活的對象。

 

作什麼:

新生代:複製清理;

老年代:標記-清除和標記-壓縮算法;

永久代:存放Java中的類和加載類的類加載器自己。

 

GC Roots都有哪些:

 

1.虛擬機棧中的引用的對象

2.方法區中靜態屬性引用的對象,常量引用的對象

3.本地方法棧中JNI(即通常說的Native方法)引用的對象。

 

Synchronized 與Lock都是可重入鎖,同一個線程再次進入同步代碼的時候.可使用本身已經獲取到的鎖。

 

Synchronized是悲觀鎖機制,獨佔鎖。而Locks.ReentrantLock是,每次不加鎖而是假設沒有衝突而去完成某項操做,若是由於衝突失敗就重試,直到成功爲止。

 

ReentrantLock適用場景

 

1.某個線程在等待一個鎖的控制權的這段時間須要中斷。

2.須要分開處理一些wait-notify,ReentrantLock裏面的Condition應用,可以控制notify哪一個線程,鎖能夠綁定多個條件。

3.具備公平鎖功能,每一個到來的線程都將排隊等候。

 

StringBuffer是線程安全的,每次操做字符串,String會生成一個新的對象,而StringBuffer不會;StringBuilder是非線程安全的。

 

fail-fast:機制是java集合(Collection)中的一種錯誤機制。當多個線程對同一個集合的內容進行操做時,就可能會產生fail-fast事件。

 

例如:當某一個線程A經過iterator去遍歷某集合的過程當中,若該集合的內容被其餘線程所改變了;那麼線程A訪問集合時,就會拋出ConcurrentModificationException異常,產生fail-fast事件。

 

happens-before:若是兩個操做之間具備happens-before 關係,那麼前一個操做的結果就會對後面一個操做可見。

 

1.程序順序規則:一個線程中的每一個操做,happens- before 於該線程中的任意後續操做。

2.監視器鎖規則:對一個監視器鎖的解鎖,happens- before 於隨後對這個監視器鎖的加鎖。

 

3.volatile變量規則:對一個volatile域的寫,happens- before於任意後續對這個volatile域的讀。

4.傳遞性:若是A happens- before B,且B happens- before C,那麼A happens- before C。

 

5.線程啓動規則:Thread對象的start()方法happens- before於此線程的每個動做。

 

Volatile和Synchronized四個不一樣點:

 

1 粒度不一樣,前者鎖對象和類,後者針對變量

2 syn阻塞,volatile線程不阻塞

3 syn保證三大特性,volatile不保證原子性

4 syn編譯器優化,volatile不優化

 

volatile具有兩種特性:

 

1.保證此變量對全部線程的可見性,指一條線程修改了這個變量的值,新值對於其餘線程來講是可見的,但並非多線程安全的。

2.禁止指令重排序優化。 

Volatile如何保證內存可見性: 

1.當寫一個volatile變量時,JMM會把該線程對應的本地內存中的共享變量刷新到主內存。 
2.當讀一個volatile變量時,JMM會把該線程對應的本地內存置爲無效。線程接下來將從主內存中讀取共享變量。

 

同步:就是一個任務的完成須要依賴另一個任務,只有等待被依賴的任務完成後,依賴任務才能完成。

 

異步:不須要等待被依賴的任務完成,只是通知被依賴的任務要完成什麼工做,只要本身任務完成了就算完成了,被依賴的任務是否完成會通知回來。(異步的特色就是通知)。

打電話和發短信來比喻同步和異步操做。

 

阻塞:CPU停下來等一個慢的操做完成之後,纔會接着完成其餘的工做。

 

非阻塞:非阻塞就是在這個慢的執行時,CPU去作其餘工做,等這個慢的完成後,CPU纔會接着完成後續的操做。

 

非阻塞會形成線程切換增長,增長CPU的使用時間能不能補償系統的切換成本須要考慮。

 

CAS(Compare And Swap) 無鎖算法:

 

CAS是樂觀鎖技術,當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程並不會被掛起,而是被告知此次競爭中失敗,並能夠再次嘗試。CAS有3個操做數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改成B,不然什麼都不作。

 

線程池的做用:

 

在程序啓動的時候就建立若干線程來響應處理,它們被稱爲線程池,裏面的線程叫工做線程。

 

第一:下降資源消耗。經過重複利用已建立的線程下降線程建立和銷燬形成的消耗。

第二:提升響應速度。當任務到達時,任務能夠不須要等到線程建立就能當即執行。

第三:提升線程的可管理性。

 

經常使用線程池:ExecutorService 是主要的實現類,其中經常使用的有

Executors.newSingleThreadPool(),newFixedThreadPool(),newcachedTheadPool(),newScheduledThreadPool()。

 

類加載器工做機制:

 

1.裝載:將Java二進制代碼導入jvm中,生成Class文件。

2.鏈接:a)校驗:檢查載入Class文件數據的正確性 b)準備:給類的靜態變量分配存儲空間 c)解析:將符號引用轉成直接引用。

3:初始化:對類的靜態變量,靜態方法和靜態代碼塊執行初始化工做。

 

雙親委派模型:類加載器收到類加載請求,首先將請求委派給父類加載器完成。

用戶自定義加載器->應用程序加載器->擴展類加載器->啓動類加載器。

 

一致性哈希:

Memcahed緩存:

數據結構:key,value對

使用方法:get,put等方法

Redis數據結構: String—字符串(key-value 類型)

 

Hash—字典(hashmap) Redis的哈希結構可使你像在數據庫中更新一個屬性同樣只修改某一項屬性值。

 

List—列表 實現消息隊列

Set—集合 利用惟一性

Sorted Set—有序集合 能夠進行排序

能夠實現數據持久化

索引:B+,B-,全文索引

 

Mysql的索引是一個數據結構,旨在使數據庫高效的查找數據。

經常使用的數據結構是B+Tree,每一個葉子節點不但存放了索引鍵的相關信息還增長了指向相鄰葉子節點的指針,這樣就造成了帶有順序訪問指針的B+Tree,作這個優化的目的是提升不一樣區間訪問的性能。

 

何時使用索引:

 

1.常常出如今group by,order by和distinc關鍵字後面的字段

2.常常與其餘表進行鏈接的表,在鏈接字段上應該創建索引

3.常常出如今Where子句中的字段

4常常出現用做查詢選擇的字段

 

Spring IOC (控制反轉,依賴注入)

 

Spring支持三種依賴注入方式,分別是屬性(Setter方法)注入,構造注入和接口注入。

在Spring中,那些組成應用的主體及由Spring IOC容器所管理的對象被稱之爲Bean。

 

Spring的IOC容器經過反射的機制實例化Bean並創建Bean之間的依賴關係。

簡單地講,Bean就是由Spring IOC容器初始化、裝配及被管理的對象。

 

獲取Bean對象的過程,首先經過Resource加載配置文件並啓動IOC容器,而後經過getBean方法獲取bean對象,就能夠調用他的方法。

 

Spring Bean的做用域:

 

Singleton:Spring IOC容器中只有一個共享的Bean實例,通常都是Singleton做用域。

Prototype:每個請求,會產生一個新的Bean實例。

Request:每一次http請求會產生一個新的Bean實例。

代理的共有優勢:業務類只須要關注業務邏輯自己,保證了業務類的重用性。

 

Java靜態代理:

 

代理對象和目標對象實現了相同的接口,目標對象做爲代理對象的一個屬性,具體接口實現中,代理對象能夠在調用目標對象相應方法先後加上其餘業務處理邏輯。

 

缺點:一個代理類只能代理一個業務類。若是業務類增長方法時,相應的代理類也要增長方法。

 

Java動態代理:

 

Java動態代理是寫一個類實現InvocationHandler接口,重寫Invoke方法,在Invoke方法能夠進行加強處理的邏輯的編寫,這個公共代理類在運行的時候才能明確本身要代理的對象,同時能夠實現該被代理類的方法的實現,而後在實現類方法的時候能夠進行加強處理。

 

實際上:代理對象的方法 = 加強處理 + 被代理對象的方法

 

JDK和CGLIB生成動態代理類的區別:

 

JDK動態代理只能針對實現了接口的類生成代理(實例化一個類)。此時代理對象和目標對象實現了相同的接口,目標對象做爲代理對象的一個屬性,具體接口實現中,能夠在調用目標對象相應方法先後加上其餘業務處理邏輯。

 

CGLIB是針對類實現代理,主要是對指定的類生成一個子類(沒有實例化一個類),覆蓋其中的方法 。

 

Spring AOP應用場景

 

性能檢測,訪問控制,日誌管理,事務等。

默認的策略是若是目標類實現接口,則使用JDK動態代理技術,若是目標對象沒有實現接口,則默認會採用CGLIB代理。

 

SpringMVC運行原理

 

1.客戶端請求提交到DispatcherServlet

2.由DispatcherServlet控制器查詢HandlerMapping,找到並分發到指定的Controller中。

3.Controller調用業務邏輯處理後,返回ModelAndView。

4.DispatcherServlet查詢一個或多個ViewResoler視圖解析器,找到ModelAndView指定的視圖。

5.視圖負責將結果顯示到客戶端。

 

一個Http請求

 

DNS域名解析 --> 發起TCP的三次握手 --> 創建TCP鏈接後發起http請求 --> 服務器響應http請求,瀏覽器獲得html代碼 --> 瀏覽器解析html代碼,並請求html代碼中的資源(如js、css、圖片等) --> 瀏覽器對頁面進行渲染呈現給用戶。

 

設計存儲海量數據的存儲系統:設計一個叫「中間層」的一個邏輯層,在這個層,將數據庫的海量數據抓出來,作成緩存,運行在服務器的內存中,同理,當有新的數據到來,也先作成緩存,再想辦法,持久化到數據庫中,這是一個簡單的思路。主要的步驟是負載均衡,將不一樣用戶的請求分發到不一樣的處理節點上,而後先存入緩存,定時向主數據庫更新數據。讀寫的過程採用相似樂觀鎖的機制,能夠一直讀(在寫數據的時候也能夠),可是每次讀的時候會有個版本的標記,若是本次讀的版本低於緩存的版本,會從新讀數據,這樣的狀況並很少,能夠忍受。

 

Session與Cookie:Cookie可讓服務端跟蹤每一個客戶端的訪問,可是每次客戶端的訪問都必須傳回這些Cookie,若是Cookie不少,則無形的增長了客戶端與服務端的數據傳輸量,而Session則很好地解決了這個問題,同一個客戶端每次和服務端交互時,將數據存儲經過Session到服務端,不須要每次都傳回全部的Cookie值,而是傳回一個ID,每一個客戶端第一次訪問服務器生成的惟一的ID,客戶端只要傳回這個ID就好了,這個ID一般爲NAME爲JSESSIONID的一個Cookie。這樣服務端就能夠經過這個ID,來將存儲到服務端的KV值取出了。

 

Session和Cookie的超時問題,Cookie的安全問題

 

分佈式Session框架

 

1.配置服務器,Zookeeper集羣管理服務器能夠統一管理全部服務器的配置文件

2.共享這些Session存儲在一個分佈式緩存中,能夠隨時寫入和讀取,並且性能要很好,如Memcache,Tair。

 

3.封裝一個類繼承自HttpSession,將session存入到這個類中而後再存入分佈式緩存中

4因爲Cookie不能跨域訪問,要實現Session同步,要同步SessionID寫到不一樣域名下。

適配器模式:將一個接口適配到另外一個接口,Java I/O中InputStreamReader將Reader類適配到InputStream,從而實現了字節流到字符流的準換。

 

裝飾者模式:保持原來的接口,加強原來有的功能。

 

FileInputStream 實現了InputStream的全部接口,BufferedInputStreams繼承自FileInputStream是具體的裝飾器實現者,將InputStream讀取的內容保存在內存中,而提升讀取的性能。

 

Spring事務配置方法:

 

1.切點信息,用於定位實施事物切面的業務類方法

2.控制事務行爲的事務屬性,這些屬性包括事物隔離級別,事務傳播行爲,超時時間,回滾規則。 


Spring經過aop/tx Schema 命名空間和@Transaction註解技術來進行聲明式事物配置。

 

Mybatis

 

每個Mybatis的應用程序都以一個SqlSessionFactory對象的實例爲核心。首先用字節流經過Resource將配置文件讀入,而後經過SqlSessionFactoryBuilder().build方法建立SqlSessionFactory,而後再經過sqlSessionFactory.openSession()方法建立一個sqlSession爲每個數據庫事務服務。

 

經歷了Mybatis初始化 -->建立SqlSession -->運行SQL語句 返回結果三個過程。

 

Servlet和Filter的區別:

 

整的流程是:Filter對用戶請求進行預處理,接着將請求交給Servlet進行處理並生成響應,最後Filter再對服務器響應進行後處理。

 

Filter有以下幾個用處:

 

Filter能夠進行對特定的url請求和相應作預處理和後處理。

在HttpServletRequest到達Servlet以前,攔截客戶的HttpServletRequest。

根據須要檢查HttpServletRequest,也能夠修改HttpServletRequest頭和數據。

在HttpServletResponse到達客戶端以前,攔截HttpServletResponse。

根據須要檢查HttpServletResponse,也能夠修改HttpServletResponse頭和數據。

 

實際上Filter和Servlet極其類似,區別只是Filter不能直接對用戶生成響應。實際上Filter裏doFilter()方法裏的代碼就是從多個Servlet的service()方法裏抽取的通用代碼,經過使用Filter能夠實現更好的複用。

 

Filter和Servlet的生命週期:

 

1.Filter在web服務器啓動時初始化

2.若是某個Servlet配置了 <load-on-startup >1 </load-on-startup >,該Servlet也是在Tomcat(Servlet容器)啓動時初始化。

3.若是Servlet沒有配置<load-on-startup >1 </load-on-startup >,該Servlet不會在Tomcat啓動時初始化,而是在請求到來時初始化。

4.每次請求, Request都會被初始化,響應請求後,請求被銷燬。

5.Servlet初始化後,將不會隨着請求的結束而註銷。

6.關閉Tomcat時,Servlet、Filter依次被註銷。

 

HashMap與HashTable的區別。

 

一、HashMap是非線程安全的,HashTable是線程安全的。

二、HashMap的鍵和值都容許有null值存在,而HashTable則不行。

三、由於線程安全的問題,HashMap效率比HashTable的要高。

 

HashMap的實現機制:

 

1.維護一個每一個元素是一個鏈表的數組,並且鏈表中的每一個節點是一個Entry[]鍵值對的數據結構。

2.實現了數組+鏈表的特性,查找快,插入刪除也快。

3.對於每一個key,他對應的數組索引下標是 int i = hash(key.hashcode)&(len-1);

4.每一個新加入的節點放在鏈表首,而後該新加入的節點指向原鏈表首。

 

HashMap和TreeMap區別

 

HashMap衝突

HashMap,ConcurrentHashMap與LinkedHashMap的區別

 

1.ConcurrentHashMap是使用了鎖分段技術技術來保證線程安全的,鎖分段技術:首先將數據分紅一段一段的存儲,而後給每一段數據配一把鎖,當一個線程佔用鎖訪問其中一個段數據的時候,其餘段的數據也能被其餘線程訪問。

 

2.ConcurrentHashMap 是在每一個段(segment)中線程安全的。

3.LinkedHashMap維護一個雙鏈表,能夠將裏面的數據按寫入的順序讀出。

 

ConcurrentHashMap應用場景

 

1:ConcurrentHashMap的應用場景是高併發,可是並不能保證線程安全,而同步的HashMap和HashMap的是鎖住整個容器,而加鎖以後ConcurrentHashMap不須要鎖住整個容器,只須要鎖住對應的Segment就行了,因此能夠保證高併發同步訪問,提高了效率。

 

2:能夠多線程寫。

 

ConcurrentHashMap把HashMap分紅若干個Segmenet

 

1.get時,不加鎖,先定位到segment而後在找到頭結點進行讀取操做。而value是volatile變量,因此能夠保證在競爭條件時保證讀取最新的值,若是讀到的value是null,則可能正在修改,那麼久調用ReadValueUnderLock函數,加鎖保證讀到的數據是正確的。

 

2.Put時會加鎖,一概添加到hash鏈的頭部。

3.Remove時也會加鎖,因爲next是final類型不可改變,因此必須把刪除的節點以前的節點都複製一遍。

 

4.ConcurrentHashMap容許多個修改操做併發進行,其關鍵在於使用了鎖分離技術。它使用了多個鎖來控制對Hash表的不一樣Segment進行的修改。

 

ConcurrentHashMap的應用場景是高併發,可是並不能保證線程安全,而同步的HashMap和HashTable的是鎖住整個容器,而加鎖以後ConcurrentHashMap不須要鎖住整個容器,只須要鎖住對應的segment就行了,因此能夠保證高併發同步訪問,提高了效率。

 

Vector和ArrayList的區別

 

ExecutorService service = Executors....

ExecutorService service = new ThreadPoolExecutor()

ExecutorService service = new ScheduledThreadPoolExecutor();

ThreadPoolExecutor源碼分析

線程池自己的狀態:

 

 

1.2 ThreadPoolExecutor 的內部工做原理

 

有了以上定義好的數據,下面來看看內部是如何實現的 。 Doug Lea 的整個思路總結起來就是 5 句話:

 

1.若是當前池大小 poolSize 小於 corePoolSize ,則建立新線程執行任務。

2.若是當前池大小 poolSize 大於 corePoolSize ,且等待隊列未滿,則進入等待隊列

3.若是當前池大小 poolSize 大於 corePoolSize 且小於 maximumPoolSize ,且等待隊列已滿,則建立新線程執行任務。

4.若是當前池大小 poolSize 大於 corePoolSize 且大於 maximumPoolSize ,且等待隊列已滿,則調用拒絕策略來處理該任務。

5.線程池裏的每一個線程執行完任務後不會馬上退出,而是會去檢查下等待隊列裏是否還有線程任務須要執行,若是在 keepAliveTime 裏等不到新的任務了,那麼線程就會退出。

 

Executor包結構

 

 

 

CopyOnWriteArrayList : 寫時加鎖,當添加一個元素的時候,將原來的容器進行copy,複製出一個新的容器,而後在新的容器裏面寫,寫完以後再將原容器的引用指向新的容器,而讀的時候是讀舊容器的數據,因此能夠進行併發的讀,但這是一種弱一致性的策略。

使用場景:CopyOnWriteArrayList適合使用在讀操做遠遠大於寫操做的場景裏,好比緩存。

 

Linux經常使用命令:cd,cp,mv,rm,ps(進程),tar,cat(查看內容),chmod,vim,find,ls

 

死鎖的必要條件

 

1.互斥 至少有一個資源處於非共享狀態

2.佔有並等待

3.非搶佔

4.循環等待 
解決死鎖,第一個是死鎖預防,就是不讓上面的四個條件同時成立。二是,合理分配資源。 


三是使用銀行家算法,若是該進程請求的資源操做系統剩餘量能夠知足,那麼就分配。

 

進程間的通訊方式

 

1.管道( pipe ):管道是一種半雙工的通訊方式,數據只能單向流動,並且只能在具備親緣關係的進程間使用。進程的親緣關係一般是指父子進程關係。

 

2.有名管道 (named pipe) : 有名管道也是半雙工的通訊方式,可是它容許無親緣關係進程間的通訊。 

3.信號量( semophore ) : 信號量是一個計數器,能夠用來控制多個進程對共享資源的訪問。它常做爲一種鎖機制,防止某進程正在訪問共享資源時,其餘進程也訪問該資源。所以,主要做爲進程間以及同一進程內不一樣線程之間的同步手段。

 

4.消息隊列( message queue ) : 消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩衝區大小受限等缺點。 

 

5.信號 ( sinal ) : 信號是一種比較複雜的通訊方式,用於通知接收進程某個事件已經發生。 

6.共享內存( shared memory ) :共享內存就是映射一段能被其餘進程所訪問的內存,這段共享內存由一個進程建立,但多個進程均可以訪問。共享內存是最快的 IPC 方式,它是針對其餘進程間通訊方式運行效率低而專門設計的。它每每與其餘通訊機制,如信號量,配合使用,來實現進程間的同步和通訊。 

7.套接字( socket ) : 套解口也是一種進程間通訊機制,與其餘通訊機制不一樣的是,它可用於不一樣機器間的進程通訊。

 

數據庫事務是指做爲單個邏輯工做單元執行的一系列操做。

 

 

 

Hibernate的一級緩存是由Session提供的,所以它只存在於Session的生命週期中,當程序調用save(),update(),saveOrUpdate()等方法 及調用查詢接口list,filter,iterate時,如Session緩存中還不存在相應的對象,Hibernate會把該對象加入到一級緩存中,當Session關閉的時候緩存也會消失。

 

Hibernate的一級緩存是Session所內置的,不能被卸載,也不能進行任何配置一級緩存採用的是key-value的Map方式來實現的,在緩存實體對象時,對象的主關鍵字ID是Map的key,實體對象就是對應的值。

 

Hibernate二級緩存:把得到的全部數據對象根據ID放入到第二級緩存中。Hibernate二級緩存策略,是針對於ID查詢的緩存策略,刪除、更新、增長數據的時候,同時更新緩存。

相關文章
相關標籤/搜索