網際網路面試知識技術總結(javacore、jvm、redis、kafka、zookeeper、mysql、hystrix)-第二章

forrunning發表於2019-04-08

0,基本資料型別:

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)聯合使用。當垃圾回收器準備回收一個物件時,如果發現它還有虛引用,就會在回收物件的記憶體之前,把這個虛引用加入到與之 關聯的引用佇列中

1,WeakReference如字面意思,弱引用, 當一個物件僅僅被weak reference(弱引用)指向, 而沒有任何其他strong reference(強引用)指向的時候, 如果這時GC執行, 那麼這個物件就會被回收,不論當前的記憶體空間是否足夠,這個物件都會被回收 2,如果一個物件只具有軟引用(SoftReference),則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些物件的記憶體。只要垃圾回收器沒有回收它,該物件就可以被程式使用。軟引用可用來實現記憶體敏感的快取記憶體

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資料進行清除)。

備註: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!

2,Arraylist初始容量為10,每次擴容1.5倍,原來元素拷貝過去,hashMap初始化容量是16,負載因子0.75,每次容量達到(0.75*上次容量)開始擴容2倍

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 (排它鎖)所組成 執行緒進入讀鎖的前提條件: 沒有其他執行緒的寫鎖, 沒有寫請求或者有寫請求,但呼叫執行緒和持有鎖的執行緒是同一個

執行緒進入寫鎖的前提條件:
沒有其他執行緒的讀鎖
沒有其他執行緒的寫鎖
複製程式碼

參考:blog.csdn.net/yupi1057/ar…

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區別。 一、攔截機制的不同   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大。 二、底層框架的不同   Struts2採用Filter(StrutsPrepareAndExecuteFilter)實現,SpringMVC(DispatcherServlet)則採用Servlet實現。Filter在容器啟動之後即初始化;服務停止以後銷燬,晚於Servlet。Servlet在是在呼叫時初始化,先於Filter呼叫,服務停止後銷燬。 三、效能方面   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種狀態。

  1. 新建狀態(New) : 執行緒物件被建立後,就進入了新建狀態。例如,Thread thread = new Thread()。
  2. 就緒狀態(Runnable): 也被稱為“可執行狀態”。執行緒物件被建立後,其它執行緒呼叫了該物件的start()方法,從而來啟動該執行緒。例如,thread.start()。處於就緒狀態的執行緒,隨時可能被CPU排程執行。
  3. 執行狀態(Running) : 執行緒獲取CPU許可權進行執行。需要注意的是,執行緒只能從就緒狀態進入到執行狀態。
  4. 阻塞狀態(Blocked) : 阻塞狀態是執行緒因為某種原因放棄CPU使用權,暫時停止執行。直到執行緒進入就緒狀態,才有機會轉到執行狀態。阻塞的情況分三種: (01) 等待阻塞 -- 通過呼叫執行緒的wait()方法,讓執行緒等待某工作的完成。 (02) 同步阻塞 -- 執行緒在獲取synchronized同步鎖失敗(因為鎖被其它執行緒所佔用),它會進入同步阻塞狀態。 (03) 其他阻塞 -- 通過呼叫執行緒的sleep()或join()或發出了I/O請求時,執行緒會進入到阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入就緒狀態。
  5. 死亡狀態(Dead) : 執行緒執行完了或者因異常退出了run()方法,該執行緒結束生命週期。 參考:www.cnblogs.com/happy-coder…

17,synchronize保證了同步程式碼塊內的共享變數可見性,volatile保證宣告的共享變數可見性 當一個變數定義為 volatile 之後,將具備兩種特性:   1.保證此變數對所有的執行緒的可見性,這裡的“可見性”,如本文開頭所述,當一個執行緒修改了這個變數的值,volatile 保證了新值能立即同步到主記憶體,以及每次使用前立即從主記憶體重新整理。但普通變數做不到這點,普通變數的值線上程間傳遞均需要通過主記憶體(詳見:Java記憶體模型)來完成。

  2.禁止指令重排序優化。有volatile修飾的變數,賦值後多執行了一個“load addl $0x0, (%esp)”操作,這個操作相當於一個記憶體屏障(指令重排序時不能把後面的指令重排序到記憶體屏障之前的位置),只有一個CPU訪問記憶體時,並不需要記憶體屏障;(什麼是指令重排序:是指CPU採用了允許將多條指令不按程式規定的順序分開傳送給各相應電路單元處理。

Volatile底層實現:Lock字首指令導致在執行指令期間,聲言處理器的 LOCK# 訊號。在多處理器環境中,LOCK# 訊號確保在聲言該訊號期間,處理器可以獨佔使用任何共享記憶體。(因為它會鎖住匯流排,導致其他CPU不能訪問匯流排,不能訪問匯流排就意味著不能訪問系統記憶體),但是在最近的處理器裡,LOCK#訊號一般不鎖匯流排,而是鎖快取,畢竟鎖匯流排開銷比較大

blog.csdn.net/u012998254/…

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(錯誤),兩者都包含了大量的異常處理類。

1、Error(錯誤):是程式中無法處理的錯誤,表示執行應用程式中出現了嚴重的錯誤。此類錯誤一般表示程式碼執行時JVM出現問題。通常有Virtual MachineError(虛擬機器執行錯誤)、NoClassDefFoundError(類定義錯誤)等。比如說當jvm耗完可用記憶體時,將出現OutOfMemoryError。此類錯誤發生時,JVM將終止執行緒。

這些錯誤是不可查的,非程式碼性錯誤。因此,當此類錯誤發生時,應用不應該去處理此類錯誤。

2、Exception(異常):程式本身可以捕獲並且可以處理的異常。 Exception這種異常又分為兩類:執行時異常和編譯異常。

1、執行時異常(不受檢異常,uncheck):RuntimeException類極其子類表示JVM在執行期間可能出現的錯誤。比如說試圖使用空值物件的引用(NullPointerException)、陣列下標越界(ArrayIndexOutBoundException)。此類異常屬於不可查異常,一般是由程式邏輯錯誤引起的,在程式中可以選擇捕獲處理,也可以不處理。

2、編譯異常(受檢異常,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觸發條件      1、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相同)。

歡迎打賞

網際網路面試知識技術總結(javacore、jvm、redis、kafka、zookeeper、mysql、hystrix)-第二章

相關文章