美團(tuán)網(wǎng)的發(fā)展速度是超乎想象的,這一在近幾年快速崛起的行業(yè),在技術(shù)方面的需求同樣發(fā)展是非常迅速的,隨著企業(yè)的運(yùn)營規(guī)模在不斷的增加,對(duì)于技術(shù)人員的數(shù)量需求也在不斷的增長。
在java方面的需求也是非常大的,下面我們就主要來看一下在美團(tuán)java方面美團(tuán)都是會(huì)問到那些問題吧。
1、任務(wù)隊(duì)列(taskQueue):用于存放沒有處理的任務(wù)。提供一種緩沖機(jī)制。
線程池技術(shù)正是關(guān)注如何縮短或調(diào)整T1,T3時(shí)間的技術(shù),從而提高服務(wù)器程序性能的。它把T1,T3分別安排在服務(wù)器程序的啟動(dòng)和結(jié)束的時(shí)間段或者一些空閑的時(shí)間段,這樣在服務(wù)器程序處理客戶請(qǐng)求時(shí),不會(huì)有T1,T3的開銷了。
線程池不僅調(diào)整T1,T3產(chǎn)生的時(shí)間段,而且它還顯著減少了創(chuàng)建線程的數(shù)目,看一個(gè)例子:
假設(shè)一個(gè)服務(wù)器一天要處理50000個(gè)請(qǐng)求,并且每個(gè)請(qǐng)求需要一個(gè)單獨(dú)的線程完成。在線程池中,線程數(shù)一般是固定的,所以產(chǎn)生線程總數(shù)不會(huì)超過線程池中線程的數(shù)目,而如果服務(wù)器不利用線程池來處理這些請(qǐng)求則線程總數(shù)為50000。一般線程池大小是遠(yuǎn)小于50000。所以利用線程池的服務(wù)器程序不會(huì)為了創(chuàng)建50000而在處理請(qǐng)求時(shí)浪費(fèi)時(shí)間,從而提高效率。
2、常見線程池
(1)newSingleThreadExecutor
單個(gè)線程的線程池,即線程池中每次只有一個(gè)線程工作,單線程串行執(zhí)行任務(wù)
(2)newFixedThreadExecutor(n)
固定數(shù)量的線程池,沒提交一個(gè)任務(wù)就是一個(gè)線程,直到達(dá)到線程池的最大數(shù)量,然后后面進(jìn)入等待隊(duì)列直到前面的任務(wù)完成才繼續(xù)執(zhí)行
(3)newCacheThreadExecutor(推薦使用)
可緩存線程池,當(dāng)線程池大小超過了處理任務(wù)所需的線程,那么就會(huì)回收部分空閑(一般是60秒無執(zhí)行)的線程,當(dāng)有任務(wù)來時(shí),又智能的添加新線程來執(zhí)行。
(4)newScheduleThreadExecutor
大小無限制的線程池,支持定時(shí)和周期性的執(zhí)行線程
3、Java 中能創(chuàng)建 volatile 數(shù)組嗎?
能,Java 中可以創(chuàng)建 volatile 類型數(shù)組,不過只是一個(gè)指向數(shù)組的引用,而不是整個(gè)數(shù)組。我的意思是,如果改變引用指向的數(shù)組,將會(huì)受到 volatile 的保護(hù),但是如果多個(gè)線程同時(shí)改變數(shù)組的元素,volatile 標(biāo)示符就不能起到之前的保護(hù)作用了。
4、volatile 能使得一個(gè)非原子操作變成原子操作嗎?
一個(gè)典型的例子是在類中有一個(gè) long 類型的成員變量。如果你知道該成員變量會(huì)被多個(gè)線程訪問,如計(jì)數(shù)器、價(jià)格等,你最好是將其設(shè)置為 volatile。為什么?因?yàn)?Java 中讀取 long 類型變量不是原子的,需要分成兩步,如果一個(gè)線程正在修改該 long 變量的值,另一個(gè)線程可能只能看到該值的一半(前 32 位)。但是對(duì)一個(gè) volatile 型的 long 或 double 變量的讀寫是原子。
5、volatile 修飾符的有過什么實(shí)踐?
一種實(shí)踐是用 volatile 修飾 long 和 double 變量,使其能按原子類型來讀寫。double 和 long 都是64位寬,因此對(duì)這兩種類型的讀是分為兩部分的,第一次讀取第一個(gè) 32 位,然后再讀剩下的 32 位,這個(gè)過程不是原子的,但 Java 中 volatile 型的 long 或 double 變量的讀寫是原子的。volatile 修復(fù)符的另一個(gè)作用是提供內(nèi)存屏障(memory barrier),例如在分布式框架中的應(yīng)用。簡單的說,就是當(dāng)你寫一個(gè) volatile 變量之前,Java 內(nèi)存模型會(huì)插入一個(gè)寫屏障(write barrier),讀一個(gè) volatile 變量之前,會(huì)插入一個(gè)讀屏障(read barrier)。意思就是說,在你寫一個(gè) volatile 域時(shí),能保證任何線程都能看到你寫的值,同時(shí),在寫之前,也能保證任何數(shù)值的更新對(duì)所有線程是可見的,因?yàn)閮?nèi)存屏障會(huì)將其他所有寫的值更新到緩存。
6、volatile 類型變量提供什么保證?
volatile 變量提供順序和可見性保證,例如,JVM 或者 JIT為了獲得更好的性能會(huì)對(duì)語句重排序,但是 volatile 類型變量即使在沒有同步塊的情況下賦值也不會(huì)與其他語句重排序。 volatile 提供 happens-before 的保證,確保一個(gè)線程的修改能對(duì)其他線程是可見的。某些情況下,volatile 還能提供原子性,如讀 64 位數(shù)據(jù)類型,像 long 和 double 都不是原子的,但 volatile 類型的 double 和 long 就是原子的。
7、闡述Session加載實(shí)體對(duì)象的過程。
Session加載實(shí)體對(duì)象的步驟是:
(1) Session在調(diào)用數(shù)據(jù)庫查詢功能之前,首先會(huì)在一級(jí)緩存中通過實(shí)體類型和主鍵進(jìn)行查找,如果一級(jí)緩存查找命中且數(shù)據(jù)狀態(tài)合法,則直接返回;
(2) 如果一級(jí)緩存沒有命中,接下來Session會(huì)在當(dāng)前NonExists記錄(相當(dāng)于一個(gè)查詢黑名單,如果出現(xiàn)重復(fù)的無效查詢可以迅速做出判斷,從而提升性能)中進(jìn)行查找,如果NonExists中存在同樣的查詢條件,則返回null;
?。?) 如果一級(jí)緩存查詢失敗則查詢二級(jí)緩存,如果二級(jí)緩存命中則直接返回;
(4) 如果之前的查詢都未命中,則發(fā)出SQL語句,如果查詢未發(fā)現(xiàn)對(duì)應(yīng)記錄則將此次查詢添加到Session的NonExists中加以記錄,并返回null;
?。?) 根據(jù)映射配置和SQL語句得到ResultSet,并創(chuàng)建對(duì)應(yīng)的實(shí)體對(duì)象;
?。?)將對(duì)象納入Session(一級(jí)緩存)的管理;
(7)如果有對(duì)應(yīng)的攔截器,則執(zhí)行攔截器的onLoad方法;
?。?) 如果開啟并設(shè)置了要使用二級(jí)緩存,則將數(shù)據(jù)對(duì)象納入二級(jí)緩存;
?。?) 返回?cái)?shù)據(jù)對(duì)象。
8、Query接口的list方法和iterate方法有什么區(qū)別?
?。?)list()方法無法利用一級(jí)緩存和二級(jí)緩存(對(duì)緩存只寫不讀),它只能在開啟查詢緩存的前提下使用查詢緩存;iterate()方法可以充分利用緩存,如果目標(biāo)數(shù)據(jù)只讀或者讀取頻繁,使用iterate()方法可以減少性能開銷。
?。?) list()方法不會(huì)引起N+1查詢問題,而iterate()方法可能引起N+1查詢問題。
9、Hibernate如何實(shí)現(xiàn)分頁查詢?
通過Hibernate實(shí)現(xiàn)分頁查詢,開發(fā)人員只需要提供HQL語句(調(diào)用Session的createQuery()方法)或查詢條件(調(diào)用Session的createCriteria()方法)、設(shè)置查詢起始行數(shù)(調(diào)用Query或Criteria接口的setFirstResult()方法)和最大查詢行數(shù)(調(diào)用Query或Criteria接口的setMaxResults()方法),并調(diào)用Query或Criteria接口的list()方法,Hibernate會(huì)自動(dòng)生成分頁查詢的SQL語句。
10、鎖機(jī)制有什么用?簡述Hibernate的悲觀鎖和樂觀鎖機(jī)制。
有些業(yè)務(wù)邏輯在執(zhí)行過程中要求對(duì)數(shù)據(jù)進(jìn)行排他性的訪問,于是需要通過一些機(jī)制保證在此過程中數(shù)據(jù)被鎖住不會(huì)被外界修改,這就是所謂的鎖機(jī)制。
Hibernate支持悲觀鎖和樂觀鎖兩種鎖機(jī)制。悲觀鎖,顧名思義悲觀的認(rèn)為在數(shù)據(jù)處理過程中極有可能存在修改數(shù)據(jù)的并發(fā)事務(wù)(包括本系統(tǒng)的其他事務(wù)或來自外部系統(tǒng)的事務(wù)),于是將處理的數(shù)據(jù)設(shè)置為鎖定狀態(tài)。悲觀鎖必須依賴數(shù)據(jù)庫本身的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性,關(guān)于數(shù)據(jù)庫的鎖機(jī)制和事務(wù)隔離級(jí)別在《Java面試題大全(上)》中已經(jīng)討論過了。樂觀鎖,顧名思義,對(duì)并發(fā)事務(wù)持樂觀態(tài)度(認(rèn)為對(duì)數(shù)據(jù)的并發(fā)操作不會(huì)經(jīng)常性的發(fā)生),通過更加寬松的鎖機(jī)制來解決由于悲觀鎖排他性的數(shù)據(jù)訪問對(duì)系統(tǒng)性能造成的嚴(yán)重影響。最常見的樂觀鎖是通過數(shù)據(jù)版本標(biāo)識(shí)來實(shí)現(xiàn)的,讀取數(shù)據(jù)時(shí)獲得數(shù)據(jù)的版本號(hào),更新數(shù)據(jù)時(shí)將此版本號(hào)加1,然后和數(shù)據(jù)庫表對(duì)應(yīng)記錄的當(dāng)前版本號(hào)進(jìn)行比較,如果提交的數(shù)據(jù)版本號(hào)大于數(shù)據(jù)庫中此記錄的當(dāng)前版本號(hào)則更新數(shù)據(jù),否則認(rèn)為是過期數(shù)據(jù)無法更新。Hibernate中通過Session的get()和load()方法從數(shù)據(jù)庫中加載對(duì)象時(shí)可以通過參數(shù)指定使用悲觀鎖;而樂觀鎖可以通過給實(shí)體類加整型的版本字段再通過XML或@Version注解進(jìn)行配置。
提示:使用樂觀鎖會(huì)增加了一個(gè)版本字段,很明顯這需要額外的空間來存儲(chǔ)這個(gè)版本字段,浪費(fèi)了空間,但是樂觀鎖會(huì)讓系統(tǒng)具有更好的并發(fā)性,這是對(duì)時(shí)間的節(jié)省。因此樂觀鎖也是典型的空間換時(shí)間的策略。
11、闡述實(shí)體對(duì)象的三種狀態(tài)以及轉(zhuǎn)換關(guān)系。
最新的Hibernate文檔中為Hibernate對(duì)象定義了四種狀態(tài)(原來是三種狀態(tài),面試的時(shí)候基本上問的也是三種狀態(tài)),分別是:瞬時(shí)態(tài)(new, or transient)、持久態(tài)(managed, or persistent)、游狀態(tài)(detached)和移除態(tài)(removed,以前Hibernate文檔中定義的三種狀態(tài)中沒有移除態(tài)),如下圖所示,就以前的Hibernate文檔中移除態(tài)被視為是瞬時(shí)態(tài)。
瞬時(shí)態(tài):當(dāng)new一個(gè)實(shí)體對(duì)象后,這個(gè)對(duì)象處于瞬時(shí)態(tài),即這個(gè)對(duì)象只是一個(gè)保存臨時(shí)數(shù)據(jù)的內(nèi)存區(qū)域,如果沒有變量引用這個(gè)對(duì)象,則會(huì)被JVM的垃圾回收機(jī)制回收。這個(gè)對(duì)象所保存的數(shù)據(jù)與數(shù)據(jù)庫沒有任何關(guān)系,除非通過Session的save()、saveOrUpdate()、persist()、merge()方法把瞬時(shí)態(tài)對(duì)象與數(shù)據(jù)庫關(guān)聯(lián),并把數(shù)據(jù)插入或者更新到數(shù)據(jù)庫,這個(gè)對(duì)象才轉(zhuǎn)換為持久態(tài)對(duì)象。
持久態(tài):持久態(tài)對(duì)象的實(shí)例在數(shù)據(jù)庫中有對(duì)應(yīng)的記錄,并擁有一個(gè)持久化標(biāo)識(shí)(ID)。對(duì)持久態(tài)對(duì)象進(jìn)行delete操作后,數(shù)據(jù)庫中對(duì)應(yīng)的記錄將被刪除,那么持久態(tài)對(duì)象與數(shù)據(jù)庫記錄不再存在對(duì)應(yīng)關(guān)系,持久態(tài)對(duì)象變成移除態(tài)(可以視為瞬時(shí)態(tài))。持久態(tài)對(duì)象被修改變更后,不會(huì)馬上同步到數(shù)據(jù)庫,直到數(shù)據(jù)庫事務(wù)提交。
游離態(tài):當(dāng)Session進(jìn)行了close()、clear()、evict()或flush()后,實(shí)體對(duì)象從持久態(tài)變成游離態(tài),對(duì)象雖然擁有持久和與數(shù)據(jù)庫對(duì)應(yīng)記錄一致的標(biāo)識(shí)值,但是因?yàn)閷?duì)象已經(jīng)從會(huì)話中清除掉,對(duì)象不在持久化管理之內(nèi),所以處于游離態(tài)(也叫脫管態(tài))。游離態(tài)的對(duì)象與臨時(shí)狀態(tài)對(duì)象是十分相似的,只是它還含有持久化標(biāo)識(shí)。