0,基本數據類型:html
byte:Java中最小的數據類型,在內存中佔8位(bit),即1個字節,取值範圍-128~127,默認值0 short:短整型,在內存中佔16位,即2個字節,取值範圍-32768~32717,默認值0 int:整型,用於存儲整數,在內在中佔32位,即4個字節,取值範圍-2147483648~2147483647,默認值0 long:長整型,在內存中佔64位,即8個字節-2^63~2^63-1,默認值0L float:浮點型,在內存中佔32位,即4個字節,用於存儲帶小數點的數字(與double的區別在於float類型有效小數點只有6~7位),默認值0 double:雙精度浮點型,用於存儲帶有小數點的數字,在內存中佔64位,即8個字節,默認值0 char:字符型,用於存儲單個字符,佔16位,即2個字節,取值範圍0~65535,默認值爲空 boolean:布爾類型,佔1個字節,用於判斷真或假(僅有兩個值,即true、false),默認值false前端
1,Java的引用類型: 強引用、弱引用、軟引用、虛引用 1,強引用是使用最廣泛的引用。若是一個對象具備強引用,那垃圾回收器毫不會回收它。 2,若是一個對象只具備軟引用,則內存空間足夠,垃圾回收器就不會回收它;若是內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就能夠被程序使用。軟引用可用來實現內存敏感的高速緩存。 3,弱引用與軟引用的區別在於:只具備弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。不過,因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快發現那些只具備弱引用的對象。 4, 「虛引用」顧名思義,就是形同虛設,與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收。 虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象的內存以前,把這個虛引用加入到與之 關聯的引用隊列中java
1,WeakReference如字面意思,弱引用, 當一個對象僅僅被weak reference(弱引用)指向, 而沒有任何其餘strong reference(強引用)指向的時候, 若是這時GC運行, 那麼這個對象就會被回收,不論當前的內存空間是否足夠,這個對象都會被回收 2,若是一個對象只具備軟引用(SoftReference),則內存空間足夠,垃圾回收器就不會回收它;若是內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就能夠被程序使用。軟引用可用來實現內存敏感的高速緩存node
3,ThreadLocal何時出現內存泄漏?ThreadLocal裏面爲啥使用了WeakReference? Thread實例爲每一個ThreadLocal對象維護了一個副本,這個副本數據存放在ThreadLocalMap裏面,所以才作到線程間的數據不共享。 <1>當一個ThreadLocal實例被直接賦值爲null(沒有調用set,remove),此時會出現內存泄漏,由於thread實例裏面的ThreadLocalMap保存了ThreadLocal的引用,假設此時線程沒有被銷燬,所以在gc的時候並不能回收這部分空間,就是說出現了內存泄漏(ThreadLocal直接賦值爲null的方式,不管使用強弱引用都沒法解決內存泄漏的問題)。 <2>若是使用弱引用(實際是ThreadLocalMap的Entry類的key才使用弱引用,value沒有使用,ThreadLocalMap裏面放就是Entry弱引用,其封裝了ThreadLocal),在ThreadLocal對象被賦值爲null,會致使弱引用在gc的時候,Entry的key被回收並變成null,使用弱引用能夠多一層保障:對應的value在下一次ThreadLocalMap調用set,get,remove的時候會被清除(這些方法的內部對Entry的key爲null的value數據進行清除)。c++
備註:ThreadLocal是一個類,當實例化一個ThreadLocal對象時,會在當前線程Thread建立一個ThreadLocalMap,這個ThreadLocalMap裏面存放了Entry,Entry是由ThreadLocal(key)和value(實際的數據)構成。Entry的key是經過弱引用封裝,若是ThreadLocal沒有外部指向(即被賦值爲null)時,那Entry的key在gc的時候就會被回收,當此線程的ThreadLocalMap被再次訪問時,會自動刪除之前Entry的key爲null的value數據。面試
參考:blog.csdn.net/wudiyong22/…算法
4,內存溢出和內存泄漏的區別 內存溢出(Out Of Memory,OOM),就是內存不夠用了,內存泄漏(Memory Leak),指的是申請的內存空間,本身沒有去主動釋放,gc也沒法釋放(如強引用),屢次內存泄漏,就會致使內存溢 memory leak會最終會致使out of memory!spring
2,Arraylist初始容量爲10,每次擴容1.5倍,原來元素拷貝過去,hashMap初始化容量是16,負載因子0.75,每次容量達到(0.75*上次容量)開始擴容2倍sql
3,線程池核心線程大小設置,機器內核數量,qps,相應時間關係 <1>若是是計算密集型的服務,因爲cpu處理效率很是高,核心線程通常設置爲內核N+1(1爲等待cpu的時間片) <2>若是是io耗時較高的服務,通常設置爲(qps*99線)/1000,其中99線爲毫秒數據庫
4,簡述線程池的實現:線程池把每一個提交到線程池的任務封裝成一個worker(而且實現了Runnable接口),當第一批任務到達的時候(corePool還沒到最大值),此時new出的線程開始執行任務,執行完,而且去消費隊列,若是coreSize滿了,此時隊列就有值了,這時候就會消費隊列裏面的任務了,其實是利用阻塞隊列的take方法維持核心線程的存活,若是隊列滿了,就會建立新線程,直至達到maxSizePool,在消費隊列中的任務數據的同時,若是線程在keepAlive時間範圍內獲取不到隊列數據,就會釋放最大線程,是經過workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)控制非核心線程的存活,若是從隊列獲取不到數據,就從worker集合刪除該線程。
5,信號量的使用:把信號量比做資源,容許多線程去使用資源,可是隻能容許部分線程使用。semaphore構造方法初始化資源大小,semaphore.acquire()獲取資源,semaphore.release()釋放資源。 CountDownLatch和Semaphore底層實現都是基於AbstractQueuedSynchronizer,CountDownLatch和Semaphore屬於典型的共享鎖。CyclicBarrier用來給多個線程之間進行互相等待其餘全部線程而設計的(而且實現了可重用機制,每次計數到0,自動從新設置爲起始值),而CountDownLatch是給一個起"調度"其它一組線程用的,這個線程關鍵職責就是等待進行其餘工做線程返回。
備註:CountDownLatch.await阻塞主線程,CountDownLatch.countDown計數變量遞減,遞減到0會喚醒主線程,cyclicBarrier.await()當作柵欄,讓一組線程都在柵欄以前完成任務,內部會作計數,只有變成0,才能讓全部線程復活。 CountDownLatch的計數器沒法被重置;CyclicBarrier的計數器能夠被重置後使用,所以它被稱爲是循環的barrier。
使用場景:CountDownLatch是一個線程等待一組子線程執行完任務,再往下執行其餘邏輯,cyclicBarrier是一組線程都達到一個臨界值,再開始作新的任務。
6,在不少狀況下,可能有多個線程須要訪問數目不多的資源。假想在服務器上運行着若干個回答客戶端請求的線程。這些線程須要鏈接到同一數據庫,但任一時刻只能得到必定數目的數據庫鏈接。你要怎樣纔可以有效地將這些固定數目的數據庫鏈接分配給大量的線程? 解決方案:好比把資源放到阻塞隊列,或者放到信號量裏面。
7,ConcurrentHashMap的原理 1.6和1.7的實現:分爲了16個segement,每一個segement內部再次實現一次hashmap,所以查找一個數據須要兩次hash(先找segement,再找segement裏面的hash位置),put操做是在segement維度使用了reentrantlock,get是先找到segement,再查找數據,找到就對整個segement加鎖。size方法是比較兩次結果,若是不相等,就對每一個segement加鎖,從新計算(爲何要比較兩次?在兩次比較的過程當中,被修改的機率很小,若是要加鎖,就會致使整個map鎖大量競爭(讀多寫少的時候),不如一開始不用鎖的方式進行比較)。 1.8:再也不使用segement,直接使用node數組+鏈表,當鏈表長度達到8,會升級爲紅黑樹。put操做是使用了synchronize對當前鏈表加鎖,get是使用Unsafe.getObjectVolatile獲取最新的共享內存值(不加鎖)。
9,Object的notify 和 notifyAll的區別 notify方法只喚醒一個等待(對象的)線程並使該線程開始執行。因此若是有多個線程等待一個對象,這個方法只會喚醒其中一個線程,選擇哪一個線程取決於操做系統對多線程管理的實現。notifyAll 會喚醒全部等待(對象的)線程,儘管哪個線程將會第一個處理取決於操做系統的實現。若是當前狀況下有多個線程須要被喚醒,推薦使用notifyAll 方法。好比在生產者-消費者裏面的使用,每次都須要喚醒全部的消費者或是生產者,以判斷程序是否能夠繼續往下執行。
notify只是喚醒正在等待的線程,至於何時開始競爭,取決於當前線程何時釋放。(ReentrantLock對應的condition.signal方法也是如此)
10,可重入鎖(ReentrantLock)的使用場景:當前線程內部邏輯進行遞歸調用
11,synchronized(獨佔鎖),多線程使用,結合object的wait、notify(notifyAll)使用時注意的問題,調用wait,是釋放當前線程持有某個對象的鎖,讓給其它線程競爭,而且由它們通知回調。 備註:使用wait、notify(notifyAll)方法前提必須是當前線程持有鎖,也就是說必須在synchronized模塊內使用 synchronized的鎖標記存放在Java對象頭的Mark Word中,同步代碼塊採用monitorenter、monitorexit指令(c++層面)顯式的實現。
12,ReentrantLock(獨佔鎖),多線程使用,結合Condition(condition = myLock.newCondition()),condition.await()和signal、signalAll()通知其它線程進行鎖的競爭。 備註:1,使用await、signal(signalAll)方法前提必須是當前線程持有鎖(也就是說一個線程不能釋放別的線程持有的鎖) 2,reentrantlock的lock方法若是獲取不到鎖,會被阻塞,tryLock獲取不到,馬上返回false,tryLock(long time, TimeUnit unit)是對獲取鎖加上時間控制 3,condition.await(),將一個線程的鎖交出,當前線程進入掛起狀態(cpu時間片交出),當前線程放入等待鎖的雙向隊列(AQS)裏面,這個線程同時也被另一個condition隊列維護,condition.signal()調用 時,將雙向隊列中的線程設置爲可搶鎖狀態,condition隊列的頭結點刪除此線程數據。 4,condition.await(),是由用戶的其它線程喚醒,condition.await(time),這是由內核在指定時間後去幫你喚醒的
13,靜態代理和動態代理區別 靜態不夠靈活,須要針對每一個被代理類的接口都對應開發一個代理類的接口,代碼維護成本比較高。 14,動態代理實現的兩種方式和區別 java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。 cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,經過修改其字節碼生成子類來處理
JDK動態代理只能對實現了接口的類生成代理,而不能針對類 CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法(繼承)
15,CGlib比JDK代理快?
(1)使用CGLib實現動態代理,CGLib底層採用ASM字節碼生成框架,使用字節碼技術生成代理類,比使用Java反射效率要高。惟一須要注意的是,CGLib不能對聲明爲final的方法進行代理,由於CGLib原理是動態生成被代理類 的子類。 (2)在對JDK動態代理與CGlib動態代理的代碼實驗中看,1W次執行下,JDK7及8的動態代理性能比CGlib要好20%左右。
16,Java 序列化作了哪些事情 Java的序列化算法通常會按步驟作以下事情: ◆將對象實例相關的類元數據輸出。 ◆遞歸地輸出類的超類描述直到再也不有超類。 ◆類元數據完了之後,開始從最頂層的超類開始輸出對象實例的實際數據值。 ◆從上至下遞歸輸出實例的數據
17,簡述公平鎖和非公平鎖的實現。 Reentrantlock支持公平和非公平模式,實現鎖的最基礎組件類是:內部類NonfairSync和FairSync,外部AbstractQueuedSynchronizer(抽象隊列同步器,AQS),公平鎖和非公平鎖在獲取鎖時都嘗試性去獲取,當獲取失敗才進入有序等待隊列中(先進先出的雙向鏈表),而且這些線程會被掛起(讓出cpu時間片),公平鎖在獲取鎖時,會判斷當前線程是不是隊列頭結點線程(hasQueuedPredecessors),若是是頭結點纔有權拿到鎖。非公平鎖在獲取鎖時,是沒在隊列中的線程和隊列的頭結點競爭(即獲取鎖時,不對線程是不是頭結點線程作限制)。當一個鎖被釋放時,它會去喚醒等待隊列中的頭結點,所以纔出現新來線程和頭結點競爭。
簡單理解:公平是按順序加鎖,非公平是不保證按順序加鎖(其實是外部線程和隊列中線程競爭),處於阻塞狀態的線程必須依賴別的線程釋放鎖,才能被喚醒去獲取鎖。
參考:cloud.tencent.com/developer/a…
18,AbstractQueuedSynchronizer爲何使用雙向隊列? aqs爲何使用雙向隊列(即雙向鏈表)的緣由,由於新進入阻塞狀態的線程要存入尾部節點,頭結點保存了尾部節點指針,這樣避免了每次尾插法都要順序遍歷一次,直接根據頭結點中的尾指針就能夠插入了,提升了入隊效率。 在移除頭結點時,下一個節點升級爲head節點時能快速與尾節點關聯起來。
19,讀寫鎖,ReentrantReadWriteLock會使用兩把鎖來解決問題,一個讀鎖,一個寫鎖 ReentrantReadWriteLock 的核心是由一個基於AQS的同步器 Sync 構成,而後由其擴展出 ReadLock (共享鎖), WriteLock (排它鎖)所組成 線程進入讀鎖的前提條件: 沒有其餘線程的寫鎖, 沒有寫請求或者有寫請求,但調用線程和持有鎖的線程是同一個
線程進入寫鎖的前提條件:
沒有其餘線程的讀鎖
沒有其餘線程的寫鎖
複製代碼
20,讀寫鎖的使用場景:讀多寫少,使用此類鎖同步機制則能夠提升併發量(www.jianshu.com/p/9f98299a1…
21,鎖降級,指的是寫鎖降級爲讀鎖,實際是持有一個寫鎖沒釋放,再去申請一個讀鎖,再釋放寫鎖,保留讀鎖,使用場景:若是當前線程不獲取讀鎖而直接釋放寫鎖,假設此刻另外一個線程(T)獲取了寫鎖並修改了數據,那麼當前線程是沒法感知線程T的數據更新,ReentrantReadWriteLock不支持鎖升級
20,爲啥覆蓋equals 時要重寫hashcode?如何重寫? 舉個例子,若是重寫equals方法,讓對象相等,可是若是不重寫hashcode,會致使使用Map結構存儲數據時,會致使相等對象存儲多個,也就是分佈在多個hash槽 重寫參考:相同屬性組成相同的hashcode
4,mq的好處:廣播式解耦合,異步化處理一下長耗時邏輯,流量削峯(上下游推送的流量很大)。
5,spring mvc一次請求經歷了什麼(SpringMVC核心處理流程)
DispatcherServlet前端控制器接收發過來的請求,交給HandlerMapping處理器映射器
HandlerMapping處理器映射器,根據請求路徑找到相應的HandlerAdapter處理器適配器(處理器適配器就是那些攔截器或Controller)
HandlerAdapter處理器適配器,處理一些功能請求,也就是真正的執行業務邏輯,返回一個ModelAndView對象(包括模型數據、邏輯視圖名)
ViewResolver視圖解析器,先根據ModelAndView中設置的View解析具體視圖
而後再將Model模型中的數據渲染到View上
這些過程都是以DispatcherServlet爲中軸線進行的。
getHandler(HandlerMapping),獲取頁面處理器,通俗點就是獲取由哪一個Controller來執行,包含方法信息以及方法參數等信息。 getHandlerAdapter(HandlerAdapter),獲取HandlerAdapter,它包含一個handle方法,負責調用真實的頁面處理器進行請求處理並返回一個ModelAndView。HandlerAdpter裏面有一些常見的處理,好比消息轉移,參數處理等,詳見此圖:裏面的argumentResolvers能夠用來處理請求的參數,messageConverts是做消息轉換等等
3,Struts和spring mvc區別。 1、攔截機制的不一樣 Struts2是類級別的攔截,每次請求就會建立一個Action,和Spring整合時Struts2的ActionBean注入做用域是原型模式prototype,而後經過setter,getter吧request數據注入到屬性。Struts2中,一個Action對應一個request,response上下文,在接收參數時,能夠經過屬性接收,這說明屬性參數是讓多個方法共享的。Struts2中Action的一個方法能夠對應一個url,而其類屬性卻被全部方法共享,這也就沒法用註解或其餘方式標識其所屬方法了,只能設計爲多例。 SpringMVC是方法級別的攔截,一個方法對應一個Request上下文,因此方法直接基本上是獨立的,獨享request,response數據。而每一個方法同時又何一個url對應,參數的傳遞是直接注入到方法中的,是方法所獨有的。處理結果經過ModeMap返回給框架。在Spring整合時,SpringMVC的Controller Bean默認單例模式Singleton,因此默認對全部的請求,只會建立一個Controller,有應爲沒有共享的屬性,因此是線程安全的,若是要改變默認的做用域,須要添加@Scope註解修改。 Struts2有本身的攔截Interceptor機制,SpringMVC這是用的是獨立的Aop方式,這樣致使Struts2的配置文件量仍是比SpringMVC大。 2、底層框架的不一樣 Struts2採用Filter(StrutsPrepareAndExecuteFilter)實現,SpringMVC(DispatcherServlet)則採用Servlet實現。Filter在容器啓動以後即初始化;服務中止之後銷燬,晚於Servlet。Servlet在是在調用時初始化,先於Filter調用,服務中止後銷燬。 3、性能方面 Struts2是類級別的攔截,每次請求對應實例一個新的Action,須要加載全部的屬性值注入,SpringMVC實現了零配置,因爲SpringMVC基於方法的攔截(更加輕量),有加載一次單例模式bean注入。因此,SpringMVC開發效率和性能高於Struts2。
4,StackOverflowError和OutofMemoryError如何發生,怎麼模擬(StackOverflowError棧溢出,如方法的遞歸調用,OutofMemoryError內存耗盡,好比不斷建立線程分配內存) 5,jvm已經發展處三種比較成熟的垃圾收集算法:1.標記-清除算法;2.複製算法;3.標記-整理算法(標記-壓縮法);4.分代收集算法,參考:www.cnblogs.com/nantang/p/5… 6,Jvm啓動參數 通常用到最多的是 -Xms512m 設置JVM促使內存爲512m。此值能夠設置與-Xmx相同,以免每次垃圾回收完成後JVM從新分配內存。 -Xmx512m ,設置JVM最大可用內存爲512M。 -Xmn200m:設置年輕代大小爲200M。整個堆大小=年輕代大小 + 年老代大小 + 持久代大小。持久代通常固定大小爲64m,因此增大年輕代後,將會減少年老代大小。此值對系統性能影響較大,Sun官方推薦配置爲整個堆的3/8(young佔30%左右) 7,gc,垃圾回收算法,經常使用的是分代收集算法(新生代和老年代分開處理),分代收集算法是複製算法和標記清除法的兩者整合 8,full gc Full GC 若是某個(些)對象(原來在內存中存活的對象或者新建立的對象)因爲以上緣由須要被移動到老年代中,而老年代中沒有足夠空間容納這個(些)對象,那麼會觸發一次Full GC,Full GC會對整個Heap進行一次GC,若是Full GC後還有沒法給新建立的對象分配內存,或者沒法移動那些須要進入老年代中的對象,那麼JVM拋出OutOfMemoryError
簡單理解gc 對象在Eden Space建立,當Eden Space滿了的時候,gc就把全部在Eden Space中的對象掃描一次,把全部有效的對象複製到第一個Survivor Space,同時把無效的對象所佔用的空間釋放。當Eden Space再次變滿了的時候,就啓動移動程序把Eden Space中有效的對象複製到第二個Survivor Space,同時,也將第一個Survivor Space中的有效對象複製到第二個Survivor Space。若是填充到第二個Survivor Space中的有效對象被第一個Survivor Space或Eden Space中的對象引用,那麼這些對象就是長期存在的,此時這些對象將被複制到Permanent Generation。若垃圾收集器依據這種小幅度的調整收集不能騰出足夠的空間,就會運行Full GC,此時JVM GC中止全部在堆中運行的線程並執行清除動做。
絕大多數剛建立的對象會被分配在Eden區,其中的大多數對象很快就會消亡。Eden區是連續的內存空間,所以在其上分配內存極快; 最初一次,當Eden區滿的時候,執行Minor GC,將消亡的對象清理掉,並將剩餘的對象複製到一個存活區Survivor0(此時,Survivor1是空白的,兩個Survivor總有一個是空白的); 下次Eden區滿了,再執行一次Minor GC,將消亡的對象清理掉,將存活的對象複製到Survivor1中,而後清空Eden區; 將Survivor0中消亡的對象清理掉,將其中能夠晉級的對象晉級到Old區,將存活的對象也複製到Survivor1區,而後清空Survivor0區; 當兩個存活區切換了幾回(HotSpot虛擬機默認15次,用-XX:MaxTenuringThreshold控制,大於該值進入老年代,但這只是個最大值,並不表明必定是這個值)以後,仍然存活的對象(其實只有一小部分,好比,咱們本身定義的對象),將被複制到老年代 參考:www.cnblogs.com/bonelee/p/8…
9,cms: PS MarkSweep:老年代收集器,是一個能夠並行標記和清理垃圾的回收器,無整理,使用的是空閒列表的方式,就像一個多線程版本的Serial Old收集器 能作到老年代提早GC的垃圾回收器有CMS收集器,但它的搭配夥伴是ParNew,由ParNew來執行新生代垃圾回收。
CMS(Concurrent Mark Sweep)收集器:老年代收集器,致力於獲取最短回收停頓時間(即縮短垃圾回收的時間),使用標記清除算法,多線程,優勢是併發收集(用戶線程能夠和GC線程同時工做),停頓小。使用-XX:+UseConcMarkSweepGC進行ParNew+CMS+Serial Old進行內存回收,優先使用ParNew+CMS(緣由見後面),當用戶線程內存不足時,採用備用方案Serial Old收集
www.cnblogs.com/zhguang/p/3… www.iteye.com/topic/11194…
10,事務是必須知足4個條件(ACID)::原子性(Atomicity,或稱不可分割性)、一致性(Consistency)、隔離性(Isolation,又稱獨立性)、持久性(Durability)。
原子性:一個事務(transaction)中的全部操做,要麼所有完成,要麼所有不完成,不會結束在中間某個環節。事務在執行過程當中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務歷來沒有執行過同樣。 一致性:在事務開始以前和事務結束之後,數據庫的完整性沒有被破壞。這表示寫入的資料必須徹底符合全部的預設規則,這包含資料的精確度、串聯性以及後續數據庫能夠自發性地完成預約的工做。 隔離性:數據庫容許多個併發事務同時對其數據進行讀寫和修改的能力,隔離性能夠防止多個事務併發執行時因爲交叉執行而致使數據的不一致。事務隔離分爲不一樣級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和串行化(Serializable)。 持久性:事務處理結束後,對數據的修改就是永久的,即使系統故障也不會丟失
11,java中的sleep()和wait()的區別 sleep()方法致使了程序暫停執行指定的時間,讓出cpu該其餘線程,可是他的監控狀態依然保持者,當指定的時間到了又會自動恢復運行狀態。 在調用sleep()方法的過程當中,線程不會釋放對象鎖。 而當調用wait()方法的時候,線程會放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用notify()方法後本線程才進入對象鎖定池準備
12,重排序 編譯期重排序的典型就是經過調整指令順序,在不改變程序語義的前提下,儘量減小寄存器的讀取、存儲次數,充分複用寄存器的存儲值。 13,happens-before原則規則: 程序次序規則:一個線程內,按照代碼順序,書寫在前面的操做先行發生於書寫在後面的操做(有依賴關係的邏輯執行前後順序是明確知道的); 鎖定規則:一個unLock操做先行發生於後面對同一個鎖額lock操做; volatile變量規則:對一個變量的寫操做先行發生於後面對這個變量的讀操做; 傳遞規則:若是操做A先行發生於操做B,而操做B又先行發生於操做C,則能夠得出操做A先行發生於操做C; 線程啓動規則:Thread對象的start()方法先行發生於此線程的每一個一個動做; 線程中斷規則:對線程interrupt()方法的調用先行發生於被中斷線程的代碼檢測到中斷事件的發生; 線程終結規則:線程中全部的操做都先行發生於線程的終止檢測,咱們能夠經過Thread.join()方法結束、Thread.isAlive()的返回值手段檢測到線程已經終止執行; 對象終結規則:一個對象的初始化完成先行發生於他的finalize()方法的開始 14,jmm: Java內存模型是圍繞着併發編程中原子性、可見性、有序性這三個特徵來創建的,www.cnblogs.com/lewis0077/p… 堆,方法區,本地方法區,方法棧,程序計數器。
15,Java中notify和notifyAll的區別 Java object提供了兩個方法notify和notifyAll來喚醒在某些條件下等待的線程,你可使用它們中的任何一個,可是Java中的notify和notifyAll之間存在細微差異,這使得它成爲Java中流行的多線程面試問題之一。當你調用notify時,只有一個等待線程會被喚醒並且它不能保證哪一個線程會被喚醒,這取決於線程調度器。雖然若是你調用notifyAll方法,那麼等待該鎖的全部線程都會被喚醒,可是在執行剩餘的代碼以前,全部被喚醒的線程都將爭奪鎖定,這就是爲何在循環上調用wait,由於若是多個線程被喚醒,那麼線程是將得到鎖定將首先執行,它可能會重置等待條件,這將迫使後續線程等待。所以,notify和notifyAll之間的關鍵區別在於notify()只會喚醒一個線程,而notifyAll方法將喚醒全部線程。
wait()方法和notify()/notifyAll()方法在放棄對象監視器的時候的區別在於:wait()方法當即釋放對象監視器,notify()/notifyAll()方法則會等待線程剩餘代碼執行完畢纔會放棄對象監視器。
備註:condition的signal方法喚醒隊列頭部的。
16,線程共包括如下5種狀態。
17,synchronize保證了同步代碼塊內的共享變量可見性,volatile保證聲明的共享變量可見性 當一個變量定義爲 volatile 以後,將具有兩種特性: 1.保證此變量對全部的線程的可見性,這裏的「可見性」,如本文開頭所述,當一個線程修改了這個變量的值,volatile 保證了新值能當即同步到主內存,以及每次使用前當即從主內存刷新。但普通變量作不到這點,普通變量的值在線程間傳遞均須要經過主內存(詳見:Java內存模型)來完成。
2.禁止指令重排序優化。有volatile修飾的變量,賦值後多執行了一個「load addl $0x0, (%esp)」操做,這個操做至關於一個內存屏障(指令重排序時不能把後面的指令重排序到內存屏障以前的位置),只有一個CPU訪問內存時,並不須要內存屏障;(什麼是指令重排序:是指CPU採用了容許將多條指令不按程序規定的順序分開發送給各相應電路單元處理。
Volatile底層實現:Lock前綴指令致使在執行指令期間,聲言處理器的 LOCK# 信號。在多處理器環境中,LOCK# 信號確保在聲言該信號期間,處理器能夠獨佔使用任何共享內存。(由於它會鎖住總線,致使其餘CPU不能訪問總線,不能訪問總線就意味着不能訪問系統內存),可是在最近的處理器裏,LOCK#信號通常不鎖總線,而是鎖緩存,畢竟鎖總線開銷比較大
18,synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(二者皆可), synchronized是java關鍵字,lock(reentrantlock)是基於cas樂觀鎖機制實現。 1)Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現; 2)synchronized在發生異常時,會自動釋放線程佔有的鎖,所以不會致使死鎖現象發生;而Lock在發生異常時,若是沒有主動經過unLock()去釋放鎖,則極可能形成死鎖現象,所以使用Lock時須要在finally塊中釋放鎖; 3)Lock可讓等待鎖的線程響應中斷,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不可以響應中斷; 4)經過Lock能夠知道有沒有成功獲取鎖,而synchronized卻沒法辦到。 5)Lock能夠提升多個線程進行讀操做的效率。 6) Lock能夠調用await方法讓出鎖資源,同時能夠調用notify通知其它線程從新獲取鎖資源,這是synchronized不具有的 在性能上來講,若是競爭資源不激烈,二者的性能是差很少的,而當競爭資源很是激烈時(即有大量線程同時競爭),此時Lock的性能要遠遠優於synchronized。因此說,在具體使用時要根據適當狀況選擇
19,公平鎖指的是線程獲取鎖的順序是按照加鎖順序來的,而非公平鎖指的是搶鎖機制,先lock的線程不必定先得到鎖。 NonfairSync和FairSync主要就是在獲取鎖的方式上不一樣,公平鎖是按順序去獲取,而非公平鎖是搶佔式的獲取,lock的時候先去嘗試修改state變量,若是搶佔成功,則獲取到鎖。 reentrantlock的實現基於AQS(AbstractQueuedSynchronizer)實現,內部經過自旋的方式完成鎖的調度,鎖的實現是基於cas(compareAndSet),參考:www.jianshu.com/p/fadac70b2…
20,ReentrantLock.lockInterruptibly容許在等待時由其它線程調用等待線程的Thread.interrupt方法來中斷等待線程的等待而直接返回,這時不用獲取鎖,而會拋出一個InterruptedException
lock 與 lockInterruptibly比較區別在於: lock 優先考慮獲取鎖,待獲取鎖成功後,才響應中斷。(此線程在運行中, 不會收到提醒,可是此線程的 「打擾標誌」會被設置, 能夠經過isInterrupted()查看並做出處理) lockInterruptibly 優先考慮響應中斷,而不是響應鎖的普通獲取或重入獲取。
可重入特性是在遞歸調用場景下,防止被調用過程阻塞 21,自定義鎖:blog.csdn.net/u012545728/… 22,java中的基本數據類型必定存儲在棧中嗎?,這句話確定是錯誤的。 基本數據類型是放在棧中仍是放在堆中,這取決於基本類型在何處聲明,下面對數據類型在內存中的存儲問題來解釋一下: 一:在方法中聲明的變量,即該變量是局部變量,每當程序調用方法時,系統都會爲該方法創建一個方法棧,其所在方法中聲明的變量就放在方法棧中,當方法結束系統會釋放方法棧,其對應在該方法中聲明的變量隨着棧的銷燬而結束,這就局部變量只能在方法中有效的緣由 在方法中聲明的變量能夠是基本類型的變量,也能夠是引用類型的變量。 (1)當聲明是基本類型的變量的時,其變量名及值(變量名及值是兩個概念)是放在JAVA虛擬機棧中 (2)當聲明的是引用變量時,所聲明的變量(該變量其實是在方法中存儲的是內存地址值)是放在JAVA虛擬機的棧中,該變量所指向的對象是放在堆類存中的。 二:在類中聲明的變量是成員變量,也叫全局變量,放在堆中的(由於全局變量不會隨着某個方法執行結束而銷燬)。 一樣在類中聲明的變量便可是基本類型的變量 也但是引用類型的變量 (1)當聲明的是基本類型的變量其變量名及其值放在堆內存中的 (2)引用類型時,其聲明的變量仍然會存儲一個內存地址值,該內存地址值指向所引用的對象。引用變量名和對應的對象仍然存儲在相應的堆中
1,爲何wait()方法和notify()/notifyAll()方法要在同步塊中被調用 這是JDK強制的,wait()方法和notify()/notifyAll()方法在調用前都必須先得到對象的鎖
2,wait()方法和notify()/notifyAll()方法在放棄對象監視器時有什麼區別 wait()方法當即釋放對象監視器,notify()/notifyAll()方法則會等待線程剩餘代碼執行完畢纔會放棄對象監視器
23,java中用到的線程調度算法是什麼 搶佔式。一個線程用完CPU以後,操做系統會根據線程優先級、線程飢餓狀況等數據算出一個總的優先級並分配下一個時間片給某個線程執行
24,java異常
Throwable:有兩個重要的子類:Exception(異常)和Error(錯誤),二者都包含了大量的異常處理類。
一、Error(錯誤):是程序中沒法處理的錯誤,表示運行應用程序中出現了嚴重的錯誤。此類錯誤通常表示代碼運行時JVM出現問題。一般有Virtual MachineError(虛擬機運行錯誤)、NoClassDefFoundError(類定義錯誤)等。好比說當jvm耗完可用內存時,將出現OutOfMemoryError。此類錯誤發生時,JVM將終止線程。
這些錯誤是不可查的,非代碼性錯誤。所以,當此類錯誤發生時,應用不該該去處理此類錯誤。
二、Exception(異常):程序自己能夠捕獲而且能夠處理的異常。 Exception這種異常又分爲兩類:運行時異常和編譯異常。
一、運行時異常(不受檢異常,uncheck):RuntimeException類極其子類表示JVM在運行期間可能出現的錯誤。好比說試圖使用空值對象的引用(NullPointerException)、數組下標越界(ArrayIndexOutBoundException)。此類異常屬於不可查異常,通常是由程序邏輯錯誤引發的,在程序中能夠選擇捕獲處理,也能夠不處理。
二、編譯異常(受檢異常,check):Exception中除RuntimeException極其子類以外的異常。若是程序中出現此類異常,好比說IOException,必須對該異常進行處理,不然編譯不經過。在程序中,一般不會自定義該類異常,而是直接使用系統提供的異常類。
25,hashMap:threshold(進行擴容時所須要的判斷基礎,初始化爲16),loadfactor是0.75,每次擴容是按照2倍擴容,擴容後threshold=table.length* loadfactor www.cnblogs.com/chengxiao/p…
26,ConcurrentHashMap則採用了不一樣的線程安全保證方式——分段鎖。它不像Hashtable那樣將整個table鎖住而是將數組元素分段加鎖,若是線程1訪問的元素在分段segment1,而線程2訪問的元素在分段segment2,則它們互不影響能夠同時進行操做。如何合理的進行分段就是其關鍵問題 a, ConcurrentHashMap在數據查找的時候,爲何要兩次hash?第一次hash是肯定segement的位置,第二次hash是肯定segement中鏈表的位置。 b,ConcurrentHashMap擴容,只擴容segement中的數組大小。
27,自旋鎖便是某一線程去嘗試獲取某個鎖時,若是該鎖已經被其餘線程佔用的話,此線程將不斷循環檢查該鎖是否被釋放,而不是讓此線程掛起或睡眠。它屬於爲了保證共享資源而提出的一種鎖機制,與互斥鎖相似,保證了公共資源在任意時刻最多隻能由一條線程獲取使用,不一樣的是互斥鎖在獲取鎖失敗後將進入睡眠或阻塞狀態
28,Comparable和Comparator區別比較 Comparable是排序接口,若一個類實現了Comparable接口,就意味着「該類支持排序」。而Comparator是比較器,咱們若須要控制某個類的次序,能夠創建一個「該類的比較器」來進行排序。 Comparable至關於「內部比較器」,而Comparator至關於「外部比較器」。 兩種方法各有優劣, 用Comparable 簡單, 只要實現Comparable 接口的對象直接就成爲一個能夠比較的對象,可是須要修改源代碼。 用Comparator 的好處是不須要修改源代碼, 而是另外實現一個比較器, 當某個自定義的對象須要做比較的時候,把比較器和對象一塊兒傳遞過去就能夠比大小了, 而且在Comparator 裏面用戶能夠本身實現複雜的能夠通用的邏輯,使其能夠匹配一些比較簡單的對象,那樣就能夠節省不少重複勞動了。 29,若是設置線程池的大小,目前業務都是io密集型的,耗時在io,所以線程池能夠設置大一些,接受更多的網絡請求,常見設置是99.9線耗時(秒)*qps,便是每一個線程每秒處理的請求
30,減小fullgc次數,原理上把大對象移到堆外,減小對堆空間的佔用,堆空間滿的時候纔會觸發fullgc,只有堆空間被寫滿的次數少了,才能減小fullgc 31,java全部的gc都會stop-the-world,包括young gc和old gc。 32,參考:www.cnblogs.com/yang-hao/p/… Minor GC觸發條件 一、eden區滿時,觸發MinorGC。即申請一個對象時,發現eden區不夠用,則觸發一次MinorGC 注:新生代分爲三個區域,eden space, from space, to space。默認比例是8:1:1。在MinorGC時,會把存活的對象複製到to space區域,若是to space區域不夠,則利用擔保機制進入老年代區域。 對eden space, from space, to space的理解:每次分配eden space空間,若是不夠,則小於 to space大小的對象複製到 to space,而後to space和from space換位置,因此咱們看到的to space一直是空的。
Full GC觸發條件 老生代空間不夠分配新的內存(old區不足以存放從young區複製過來的對象)
33,EHCache(Terrcotta BigMemory)的 off-heap(堆外內存,操做系統層面的堆外內存,不受gc影響)將你的對象從堆中脫離出來序列化,而後存儲在一大塊內存中,這就像它存儲到磁盤上上同樣,但它仍然在RAM中 34,Java緩存類型 2.1 堆內緩存 使用Java堆內存來存儲對象。可使用Guava Cache、Ehcache、MapDB實現。 優勢:使用堆緩存的好處是沒有序列化/反序列化,是最快的緩存; 缺點:很明顯,當緩存的數據量很大時, GC暫停時間會變長,存儲容量受限於堆空間大小;通常經過軟引用/弱引用來存儲緩存對象,即當堆內存不足時,能夠強制回收這部份內存釋放堆內存空間。通常使用堆緩存存儲較熱的數據。 2.2 堆外緩存 即緩存數據存儲在堆外內存。可使用Ehcache 3.x、MapDB實現。
優勢:能夠減小GC暫停時間(堆對象轉移到堆外,GC掃描和移動的對象變少了),能夠支持更大的緩存空間(只受機器內存大小限制,不受堆空間的影響)。 缺點:讀取數據時須要序列化/反序列化,會比堆緩存慢不少
34,java的本地緩存類型 1,分爲堆內和堆內兩種類型的緩存數據,常見的堆內緩存:如hashMap,或者guavacache,它們都收到jvm gc的影響。堆外內存有兩種類型:受jvm gc影響的堆外緩存和操做系統層面的堆外緩存,受gc影響的堆外內存能夠用過nio的DirectByteBuffer申請內存空間,不受gc影響的堆外內存能夠經過ehcache(堆外,堆內,文件模式都支持)管理 2,DirectByteBuffer(調用unsafe的native方法申請分配內存)申請的內存空間是堆外內存,這塊內存的地址會被Cleaner持有(ByteBuffer.allocateDirect,分配內存時,將這塊的內存地址給Cleaner),在gc的時候,若是這塊內存空間出現無引用以後,就會被釋放,也就是說這塊內存空間是受到gc影響的
Cleaner類繼承自PhantomReference< Object>在此處保留Cleaner對象的虛引用。此類中還包含一個靜態DirectByteBuffer引用隊列用於得知那些虛引用所指向的對象已回收,這是一個很棒的設計由於jvm不知道堆外內存的使用狀況,經過DirectByteBuffer對象的回收來間接控制堆外內存的回收。 參考:blog.csdn.net/Big_Blogger…
35,類加載器(www.importnew.com/6581.html,h… 類加載機制,簡單理解就是委託、可見性和單一性。
<1>Bootstrap類加載器負責加載rt.jar中的JDK類文件,它是全部類加載器的父加載器
<2>Extension將加載類的請求先委託給它的父加載器,也就是Bootstrap,若是沒有成功加載的話,再從jre/lib/ext目錄下或者java.ext.dirs系統屬性定義的目錄下加載類。Extension加載器由sun.misc.Launcher$ExtClassLoader實現。這爲引入除Java核心類之外的新功能提供了一個標準機制
<3>System類加載器(又叫做Application類加載器),它負責從classpath環境變量中加載某些應用相關的類,Application類加載器是Extension類加載器的子加載器。經過sun.misc.Launcher$AppClassLoader實現 <4>自定義加載器,MyClassLoader extends ClassLoader,通常只須要重寫findClass(從別的地方獲取類文件),最好不要重寫loadClass方法,由於這樣容易破壞雙親委託模式。
Java類加載器的做用就是在運行時加載類。Java類加載器基於三個機制:委託、可見性和單一性。委託機制是指將加載一個類的請求交給父類加載器,若是這個父類加載器不可以找到或者加載這個類,那麼再加載它。可見性的原理是子類的加載器能夠看見全部的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。單一性原理是指僅加載一個類一次,這是由委託機制確保子類加載器不會再次加載父類加載器加載過的類。正確理解類加載器可以幫你解決NoClassDefFoundError和java.lang.ClassNotFoundException,由於它們和類的加載相關。類加載器一般也是比較高級的Java面試中的重要考題,Java類加載器和工做原理以及classpath如何運做的常常被問到。Java面試題中也常常出現「一個類是否能被兩個不一樣類加載器加載」這樣的問題。這篇教程中,咱們將學到類加載器是什麼,它的工做原理以及一些關於類加載器的知識點。
36, (1)阿里的面試官問我,能夠不能夠本身寫個String類
答案:不能夠,由於 根據類加載的雙親委派機制,會去加載父類,父類發現衝突了String就再也不加載了;
(2)可否在加載類的時候,對類的字節碼進行修改
答案:能夠,使用Java探針技術,能夠參考:Java探針-Java Agent技術-阿里面試題
(3)如何實現熱部署:自定義classLoader就能夠了,熱部署以前,銷燬(即gc回收掉)以前部署的classLoader
37,class什麼時候觸發初始化
爲一個類型建立一個新的對象實例時(好比new、反射、序列化) 調用一個類型的靜態方法時(即在字節碼中執行invokestatic指令) 調用一個類型或接口的靜態字段,或者對這些靜態字段執行賦值操做時(即在字節碼中,執行getstatic或者putstatic指令),不過用final修飾的靜態字段除外,它被初始化爲一個編譯時常量表達式 調用JavaAPI中的反射方法時(好比調用java.lang.Class中的方法,或者java.lang.reflect包中其餘類的方法) 初始化一個類的派生類時(Java虛擬機規範明確要求初始化一個類時,它的超類必須提早完成初始化操做,接口例外) JVM啓動包含main方法的啓動類時。
38,數據庫鏈接池簡單實現,參考:blog.csdn.net/moakun/arti… public class SimplePoolDemo { //建立一個鏈接池 private static LinkedList pool = new LinkedList();
//初始化10個鏈接
static{
try {
for (int i = 0; i < 10; i++) {
Connection conn = DBUtils.getConnection();//獲得一個鏈接
pool.add(conn);
}
} catch (Exception e) {
throw new ExceptionInInitializerError("數據庫鏈接失敗,請檢查配置");
}
}
//從池中獲取一個鏈接
public static Connection getConnectionFromPool(){
return pool.removeFirst();//移除一個鏈接對象
}
//釋放資源
public static void release(Connection conn){
pool.addLast(conn);
}
複製代碼
}
C3p0,dbcp,druid的區別: c3p0有自動回收空閒鏈接功能,dbcp沒有自動的去回收空閒鏈接的功能
C3P0提供最大空閒時間,DBCP提供最大連數。 Druid具有的功能更加豐富,還具有sql注入的語法校驗。 參考:mp.weixin.qq.com/s?__biz=MzI…
Dbcp源碼解讀:www.jianshu.com/p/f430c1d13… Dbcp源碼讀後總結: dbcp有個定時器(基於定時器實現)去保證鏈接池維持一個稱做minIdle狀態(最小閒置狀態,若是是無併發場景,minIdle爲1就夠了),大部分狀況下都超過minIdle,由於數據庫訪問頻率都很高的,當訪問量增長的時候,會建立鏈接直至最大值爲maxActive,若是鏈接數超過maxActive,請求會被阻塞(分爲永久阻塞和限時阻塞,可配置最長等待時間maxWait)。當qps降下來,鏈接數不須要那麼多的時候,會保持鏈接數在maxIdle(最大閒置數),多餘的會被銷燬(若是maxIdle==maxActive,就不會出現銷燬了,所以生產環境通常配置maxIdle和maxActive相同)。
歡迎打賞