一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

工作兩年多,XX 征信 面試,offer已到手

 田維常 2021-07-13

回復(fù)“000”獲取程序員必備電子書

大家好,我是老田,今天給大家分享的是一位兩年多工作經(jīng)驗(yàn)的小伙伴面試經(jīng)歷,恭喜他成功上岸,收到了offer!本文大部分內(nèi)容是這位朋友所寫,我對(duì)一小部分內(nèi)容進(jìn)行修正和調(diào)整,話不多說(shuō),咱們直入主題。

面試官到了后,看著簡(jiǎn)歷,然后來(lái)一句千年不變的:先做一個(gè)自我介紹,然后我就大致做了一個(gè)簡(jiǎn)短的自我介紹。隨后面試官,隨便問了為什么離職?還問了項(xiàng)目情況,最后開始技術(shù)面試。

1、說(shuō)說(shuō)你對(duì)HashMap的理解

這道題,有點(diǎn)泛,所以只能把自己知道都回答一遍,能回答越多越好。

關(guān)于這道題,我們可以從幾個(gè)方面去回答:

數(shù)據(jù)結(jié)構(gòu)

JDK1.7之前采用的是數(shù)組+鏈表。

JDK1.8后采用的是數(shù)組+鏈表(紅黑樹),當(dāng)鏈表的長(zhǎng)度大于8,并且數(shù)組長(zhǎng)度為64時(shí),如果再往此鏈表上添加數(shù)據(jù),那么該鏈表就會(huì)轉(zhuǎn)為紅黑樹。

put方法過(guò)程

看流程圖,這樣印象更深刻:


線程安全問題

在多線程環(huán)境下,1.7 會(huì)產(chǎn)生死循環(huán)、數(shù)據(jù)丟失、數(shù)據(jù)覆蓋的問題,1.8 中會(huì)有數(shù)據(jù)覆蓋的問題,以 1.8 為例,當(dāng) A 線程判斷 index 位置為空后正好掛起,B 線程開始往 index 位置的寫入節(jié)點(diǎn)數(shù)據(jù),這時(shí) A 線程恢復(fù)現(xiàn)場(chǎng),執(zhí)行賦值操作,就把 A 線程的數(shù)據(jù)給覆蓋了;還有++size 這個(gè)地方也會(huì)造成多線程同時(shí)擴(kuò)容等問題。

2、hash沖突解決方案有哪些

這個(gè)題是知道就知道,不知道就是不知道,理論性很強(qiáng),說(shuō)白了背就得了。

一共有四種方法:

1、再哈希法:如果hash出的index已經(jīng)有值,就再hash,不行繼續(xù)hash,直至找到空的index位置,要相信瞎貓總能碰上死耗子。這個(gè)辦法最容易想到。但有2個(gè)缺點(diǎn):

  • 比較浪費(fèi)空間,消耗效率。根本原因還是數(shù)組的長(zhǎng)度是固定不變的,不斷hash找出空的index,可能越界,這時(shí)就要?jiǎng)?chuàng)建新數(shù)組,而老數(shù)組的數(shù)據(jù)也需要遷移。隨著數(shù)組越來(lái)越大,消耗不可小覷。
  • get不到,或者說(shuō)get算法復(fù)雜。進(jìn)是進(jìn)去了,想出來(lái)就沒那么容易了。

2、開放地址方法:如果hash出的index已經(jīng)有值,通過(guò)算法在它前面或后面的若干位置尋找空位,這個(gè)和再hash算法差別不大。

3、建立公共溢出區(qū): 把沖突的hash值放到另外一塊溢出區(qū)。

4、鏈?zhǔn)降刂贩ǎ?/strong> 把產(chǎn)生hash沖突的hash值以鏈表形式存儲(chǔ)在index位置上。HashMap用的就是該方法。優(yōu)點(diǎn)是不需要另外開辟新空間,也不會(huì)丟失數(shù)據(jù),尋址也比較簡(jiǎn)單。但是隨著hash鏈越來(lái)越長(zhǎng),尋址也是更加耗時(shí)。好的hash算法就是要讓鏈盡量短,最好一個(gè)index上只有一個(gè)值。也就是盡可能地保證散列地址分布均勻,同時(shí)要計(jì)算簡(jiǎn)單。

3、說(shuō)說(shuō)你對(duì)Spring IOC的理解

傳說(shuō)中的傻屌面試題,但是又不得不問的面試題,所以一定要回答的好,給面試官一個(gè)好的印象。

IOC就是控制反轉(zhuǎn),是指創(chuàng)建對(duì)象的控制權(quán)的轉(zhuǎn)移。以前創(chuàng)建對(duì)象的主動(dòng)權(quán)和時(shí)機(jī)是由自己把控的,而現(xiàn)在這種權(quán)力轉(zhuǎn)移到Spring容器中,并由容器根據(jù)配置文件去創(chuàng)建實(shí)例和管理各個(gè)實(shí)例之間的依賴關(guān)系。對(duì)象與對(duì)象之間松散耦合,也利于功能的復(fù)用。DI依賴注入,和控制反轉(zhuǎn)是同一個(gè)概念的不同角度的描述,即 應(yīng)用程序在運(yùn)行時(shí)依賴IoC容器來(lái)動(dòng)態(tài)注入對(duì)象需要的外部資源。

最直觀的表達(dá)就是,IOC讓對(duì)象的創(chuàng)建不用去new了,可以由spring自動(dòng)生產(chǎn),使用java的反射機(jī)制,根據(jù)配置文件在運(yùn)行時(shí)動(dòng)態(tài)的去創(chuàng)建對(duì)象以及管理對(duì)象,并調(diào)用對(duì)象的方法的。

Spring的IOC有三種注入方式 :構(gòu)造器注入、setter方法注入、根據(jù)注解注入。

IoC讓相互協(xié)作的組件保持松散的耦合,而AOP編程允許你把遍布于應(yīng)用各層的功能分離出來(lái)形成可重用的功能組件。

4、Spring AOP在工作中有用過(guò)嗎?

應(yīng)該是后端開發(fā)必備,不管是吹牛逼,還是實(shí)戰(zhàn),這都是必備的。

有用過(guò)。

AOP(Aspect-Oriented Programming,面向切面編程)能夠?qū)⒛切┡c業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任(例如事務(wù)處理、日志管理、權(quán)限控制等)封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度,并有利于未來(lái)的可擴(kuò)展性和可維護(hù)性。

Spring AOP是基于動(dòng)態(tài)代理的,如果要代理的對(duì)象實(shí)現(xiàn)了某個(gè)接口,那么Spring AOP就會(huì)使用JDK動(dòng)態(tài)代理去創(chuàng)建代理對(duì)象;而對(duì)于沒有實(shí)現(xiàn)接口的對(duì)象,就無(wú)法使用JDK動(dòng)態(tài)代理,轉(zhuǎn)而使用CGlib動(dòng)態(tài)代理生成一個(gè)被代理對(duì)象的子類來(lái)作為代理。

當(dāng)然也可以使用AspectJ,Spring AOP中已經(jīng)集成了AspectJ,AspectJ應(yīng)該算得上是Java生態(tài)系統(tǒng)中最完整的AOP框架了。使用AOP之后我們可以把一些通用功能抽象出來(lái),在需要用到的地方直接使用即可,這樣可以大大簡(jiǎn)化代碼量。我們需要增加新功能也方便,提高了系統(tǒng)的擴(kuò)展性。日志功能、事務(wù)管理和權(quán)限管理等場(chǎng)景都用到了AOP。

5、@Controller和@RestController有什么區(qū)別?

這是個(gè)基礎(chǔ)性的問題,但天天都在用,卻沒有關(guān)心過(guò)兩者的區(qū)別。

@RestControllerspring 4.0.1版本后新增的內(nèi)容,@Controllerspring 2.5.0版本后新增的內(nèi)容。兩者在實(shí)際使用中都用于定義控制層,用于控制業(yè)務(wù)邏輯層的跳轉(zhuǎn)。

對(duì)比源碼可知@RestController相對(duì)@Controller增加了@RestponseBody注釋。

@RestController相對(duì)于@Controller增加了@ResponseBody返回機(jī)制。相當(dāng)于@Controller+@ResponseBody兩個(gè)注解的結(jié)合,返回json數(shù)據(jù)不需要在方法前面加@ResponseBody注解了,但使用@RestController這個(gè)注解,就不能返回jsp,html頁(yè)面,視圖解析器無(wú)法解析jsp,html頁(yè)面。

6、熟悉Bean的生命周期嗎?

造輪子必備,寫碼業(yè)務(wù)代碼的基本上不太關(guān)心這種,但是面試被問概率卻是相當(dāng)?shù)母摺?/span>

首先說(shuō)一下Servlet的生命周期:實(shí)例化,初始init,接收請(qǐng)求service,銷毀destroy;

Spring上下文中的Bean生命周期也類似,如下:

(1)實(shí)例化Bean:

對(duì)于BeanFactory容器,當(dāng)客戶向容器請(qǐng)求一個(gè)尚未初始化的bean時(shí),或初始化bean的時(shí)候需要注入另一個(gè)尚未初始化的依賴時(shí),容器就會(huì)調(diào)用createBean進(jìn)行實(shí)例化。對(duì)于ApplicationContext容器,當(dāng)容器啟動(dòng)結(jié)束后,通過(guò)獲取BeanDefinition對(duì)象中的信息,實(shí)例化所有的bean。

(2)設(shè)置對(duì)象屬性(依賴注入):

實(shí)例化后的對(duì)象被封裝在BeanWrapper對(duì)象中,緊接著,Spring根據(jù)BeanDefinition中的信息 以及 通過(guò)BeanWrapper提供的設(shè)置屬性的接口完成依賴注入。

(3)處理Aware接口:

接著,Spring會(huì)檢測(cè)該對(duì)象是否實(shí)現(xiàn)了xxxAware接口,并將相關(guān)的xxxAware實(shí)例注入給Bean:

①如果這個(gè)Bean已經(jīng)實(shí)現(xiàn)了BeanNameAware接口,會(huì)調(diào)用它實(shí)現(xiàn)的setBeanName(String beanId)方法,此處傳遞的就是Spring配置文件中Bean的id值;

②如果這個(gè)Bean已經(jīng)實(shí)現(xiàn)了BeanFactoryAware接口,會(huì)調(diào)用它實(shí)現(xiàn)的setBeanFactory()方法,傳遞的是Spring工廠自身。

③如果這個(gè)Bean已經(jīng)實(shí)現(xiàn)了ApplicationContextAware接口,會(huì)調(diào)用setApplicationContext(ApplicationContext)方法,傳入Spring上下文;

(4)BeanPostProcessor:

如果想對(duì)Bean進(jìn)行一些自定義的處理,那么可以讓Bean實(shí)現(xiàn)了BeanPostProcessor接口,那將會(huì)調(diào)用postProcessBeforeInitialization(Object obj, String s)方法。

(5)InitializingBean 與 init-method:

如果Bean在Spring配置文件中配置了 init-method 屬性,則會(huì)自動(dòng)調(diào)用其配置的初始化方法。

(6)如果這個(gè)Bean實(shí)現(xiàn)了BeanPostProcessor接口,將會(huì)調(diào)用postProcessAfterInitialization(Object obj, String s)方法;由于這個(gè)方法是在Bean初始化結(jié)束時(shí)調(diào)用的,所以可以被應(yīng)用于內(nèi)存或緩存技術(shù);

以上幾個(gè)步驟完成后,Bean就已經(jīng)被正確創(chuàng)建了,之后就可以使用這個(gè)Bean了。

(7)DisposableBean:

當(dāng)Bean不再需要時(shí),會(huì)經(jīng)過(guò)清理階段,如果Bean實(shí)現(xiàn)了DisposableBean這個(gè)接口,會(huì)調(diào)用其實(shí)現(xiàn)的destroy()方法;

(8)destroy-method:

最后,如果這個(gè)Bean的Spring配置中配置了destroy-method屬性,會(huì)自動(dòng)調(diào)用其配置的銷毀方法。


7、說(shuō)說(shuō)Synchronized和ReentrantLock的區(qū)別

看樣子是在考察一個(gè)很平常的題目,但是面試官可能會(huì)因?yàn)槟慊卮鹆藘烧邊^(qū)別后,引發(fā)其他相關(guān)問題,也有可能葬送了大好機(jī)會(huì)。

相似點(diǎn)

這兩種同步方式有很多相似之處,它們都是加鎖方式同步,而且都是阻塞式的同步,也就是說(shuō)當(dāng)如果一個(gè)線程獲得了對(duì)象鎖,進(jìn)入了同步塊,其他訪問該同步塊的線程都必須阻塞在同步塊外面等待,而進(jìn)行線程阻塞和喚醒的代價(jià)是比較高的.

區(qū)別

這兩種方式最大區(qū)別就是對(duì)于Synchronized來(lái)說(shuō),它是java語(yǔ)言的關(guān)鍵字,是原生語(yǔ)法層面的互斥,需要jvm實(shí)現(xiàn)。而ReentrantLock它是JDK 1.5之后提供的API層面的互斥鎖,需要lock()和unlock()方法配合try/finally語(yǔ)句塊來(lái)完成。

synchronized經(jīng)過(guò)編譯,會(huì)在同步塊的前后分別形成monitorenter和monitorexit這個(gè)兩個(gè)字節(jié)碼指令。在執(zhí)行monitorenter指令時(shí),首先要嘗試獲取對(duì)象鎖。如果這個(gè)對(duì)象沒被鎖定,或者當(dāng)前線程已經(jīng)擁有了那個(gè)對(duì)象鎖,把鎖的計(jì)算器加1,相應(yīng)的,在執(zhí)行monitorexit指令時(shí)會(huì)將鎖計(jì)算器就減1,當(dāng)計(jì)算器為0時(shí),鎖就被釋放了。如果獲取對(duì)象鎖失敗,那當(dāng)前線程就要阻塞,直到對(duì)象鎖被另一個(gè)線程釋放為止。

由于ReentrantLock是java.util.concurrent包下提供的一套互斥鎖,相比Synchronized,ReentrantLock類提供了一些高級(jí)功能,主要有以下3項(xiàng):

1.等待可中斷,持有鎖的線程長(zhǎng)期不釋放的時(shí)候,正在等待的線程可以選擇放棄等待,這相當(dāng)于Synchronized來(lái)說(shuō)可以避免出現(xiàn)死鎖的情況。

2.公平鎖,多個(gè)線程等待同一個(gè)鎖時(shí),必須按照申請(qǐng)鎖的時(shí)間順序獲得鎖,Synchronized鎖非公平鎖,ReentrantLock默認(rèn)的構(gòu)造函數(shù)是創(chuàng)建的非公平鎖,可以通過(guò)參數(shù)true設(shè)為公平鎖,但公平鎖表現(xiàn)的性能不是很好。

3.鎖綁定多個(gè)條件,一個(gè)ReentrantLock對(duì)象可以同時(shí)綁定對(duì)個(gè)對(duì)象。

8、了解volatile關(guān)鍵字嗎?

這種題目,簡(jiǎn)單也可以說(shuō),詳細(xì)點(diǎn)也可以說(shuō),深層次的也可以說(shuō)。就看面試官需要你回答那個(gè)層次。

一旦一個(gè)共享變量(類的成員變量、類的靜態(tài)成員變量)被volatile修飾之后,那么就具備了兩層語(yǔ)義:

  • 1.保證了不同線程對(duì)這個(gè)變量進(jìn)行操作時(shí)的可見性,即一個(gè)線程修改了某個(gè)變量的值,這新值對(duì)其他線程來(lái)說(shuō)是立即可見的。2.禁止進(jìn)行指令重排序。
  • volatile本質(zhì)是在告訴jvm當(dāng)前變量在寄存器(工作內(nèi)存)中的值是不確定的,需要從主存中讀取;synchronized則是鎖定當(dāng)前變量,只有當(dāng)前線程可以訪問該變量,其他線程被阻塞住。
  • volatile僅能使用在變量級(jí)別;synchronized則可以使用在變量、方法、和類級(jí)別的。
  • volatile僅能實(shí)現(xiàn)變量的修改可見性,并不能保證原子性;synchronized則可以保證變量的修改可見性和原子性。
  • volatile不會(huì)造成線程的阻塞;synchronized可能會(huì)造成線程的阻塞。

volatile標(biāo)記的變量不會(huì)被編譯器優(yōu)化;synchronized標(biāo)記的變量可以被編譯器優(yōu)化。

9、說(shuō)說(shuō)你對(duì)并發(fā)編程中CAS的理解

千萬(wàn)別把并發(fā)編程中的CAS和單點(diǎn)登錄搞混了哈

CAS叫做CompareAndSwap,比較并交換,主要是通過(guò)處理器的指令來(lái)保證操作的原子性,它包含三個(gè)操作數(shù):

  1. 變量?jī)?nèi)存地址,V表示
  2. 舊的預(yù)期值,A表示
  3. 準(zhǔn)備設(shè)置的新值,B表示

當(dāng)執(zhí)行CAS指令時(shí),只有當(dāng)V等于A時(shí),才會(huì)用B去更新V的值,否則就不會(huì)執(zhí)行更新操作。

CAS的缺點(diǎn)主要有3點(diǎn)

ABA問題:ABA的問題指的是在CAS更新的過(guò)程中,當(dāng)讀取到的值是A,然后準(zhǔn)備賦值的時(shí)候仍然是A,但是實(shí)際上有可能A的值被改成了B,然后又被改回了A,這個(gè)CAS更新的漏洞就叫做ABA。只是ABA的問題大部分場(chǎng)景下都不影響并發(fā)的最終效果。

Java中有AtomicStampedReference來(lái)解決這個(gè)問題,他加入了預(yù)期標(biāo)志和更新后標(biāo)志兩個(gè)字段,更新時(shí)不光檢查值,還要檢查當(dāng)前的標(biāo)志是否等于預(yù)期標(biāo)志,全部相等的話才會(huì)更新。

循環(huán)時(shí)間長(zhǎng)開銷大:自旋CAS的方式如果長(zhǎng)時(shí)間不成功,會(huì)給CPU帶來(lái)很大的開銷。

只能保證一個(gè)共享變量的原子操作:只對(duì)一個(gè)共享變量操作可以保證原子性,但是多個(gè)則不行,多個(gè)可以通過(guò)AtomicReference來(lái)處理或者使用鎖synchronized實(shí)現(xiàn)。

9、線程有哪些狀態(tài)?

這個(gè)題目網(wǎng)上答案都有些差別,有的說(shuō)是5個(gè)有的是6個(gè)。很多是跟著別人寫,但是寫偏了。五種有五種的說(shuō)法,六種有六種的說(shuō)法,我們是Java開發(fā),肯定關(guān)注的是Java中線程的狀態(tài),并且在Thread類中有個(gè)內(nèi)部類State就是表示線程狀態(tài)的,注釋里還有每個(gè)狀態(tài)的相關(guān)解釋。


Java中線程的狀態(tài)分為6種。

  1. 初始(NEW):新創(chuàng)建了一個(gè)線程對(duì)象,但還沒有調(diào)用start()方法。
  2. 運(yùn)行(RUNNABLE):Java線程中將就緒(ready)和運(yùn)行中(running)兩種狀態(tài)籠統(tǒng)的稱為“運(yùn)行”。線程對(duì)象創(chuàng)建后,其他線程(比如main線程)調(diào)用了該對(duì)象的start()方法。該狀態(tài)的線程位于可運(yùn)行線程池中,等待被線程調(diào)度選中,獲取CPU的使用權(quán),此時(shí)處于就緒狀態(tài)(ready)。就緒狀態(tài)的線程在獲得CPU時(shí)間片后變?yōu)檫\(yùn)行中狀態(tài)(running)。
  3. 阻塞(BLOCKED):表示線程阻塞于鎖。
  4. 等待(WAITING):進(jìn)入該狀態(tài)的線程需要等待其他線程做出一些特定動(dòng)作(通知或中斷)。
  5. 超時(shí)等待(TIMED_WAITING):該狀態(tài)不同于WAITING,它可以在指定的時(shí)間后自行返回。
  6. 終止(TERMINATED):表示該線程已經(jīng)執(zhí)行完畢。

想獲取面試官的青睞,還是得說(shuō)說(shuō)線程的狀態(tài)流轉(zhuǎn),可以根據(jù)下面這張圖來(lái)描述:


10、有用過(guò)線程池嗎?是怎么用的?

這個(gè)線程池在面試中也基本上是必問的題目。

有用過(guò),

創(chuàng)建線程有兩種方式

  • ThreadPoolExecutor
  • Executors

使用ThreadPoolExecutor是JDK原生態(tài)創(chuàng)建線程池,也可以使用Executors工具類來(lái)創(chuàng)建線程池,并Executors大多數(shù)都是基于ThreadPoolExecutor進(jìn)行二次封裝。

以下是Executors方式創(chuàng)建線程池的幾種方式:

  • newSingleThreadExecutor:創(chuàng)建一個(gè)單線程的線程池,此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。
  • newFixedThreadPool:創(chuàng)建固定大小的線程池,每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小。
  • newCachedThreadPool:創(chuàng)建一個(gè)可緩存的線程池,此線程池不會(huì)對(duì)線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說(shuō)JVM)能夠創(chuàng)建的最大線程大小。
  • newScheduledThreadPool:創(chuàng)建一個(gè)大小無(wú)限的線程池,此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。
  • newSingleThreadExecutor:創(chuàng)建一個(gè)單線程的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。
  • newWorkStealingPoo0l:Java 8 新增創(chuàng)建線程池的方法,創(chuàng)建時(shí)如果不設(shè)置任何參數(shù),則以當(dāng)前機(jī)器CPU 處理器數(shù)作為線程個(gè)數(shù),此線程池會(huì)并行處理任務(wù),不能保證執(zhí)行順序。

通常不建議使用Executors來(lái)創(chuàng)建線程池,因?yàn)樵摲绞街泻芏鄥?shù)都已經(jīng)給你設(shè)置好了,所以在使用的時(shí)候,如果使用不當(dāng)或者對(duì)參數(shù)沒有認(rèn)證考察可能會(huì)產(chǎn)生很多意想不到的問題:比如隊(duì)列多大,造成OOM等。所以,通常都建議使用ThreadPoolExecutor創(chuàng)建線程池。

11、說(shuō)說(shuō)線程池中那幾個(gè)核心參數(shù)和含義

這道題目,基本上快成必考面試題了,不過(guò)我還是記住了,還有就是在某些面試中,喜歡問線程池的原理,其實(shí)也是側(cè)面的讓你講解線程池的這些核心參數(shù)的含義。

corePoolSize:核心線程數(shù)

線程池維護(hù)的最小線程數(shù)量,核心線程創(chuàng)建后不會(huì)被回收(注意:設(shè)置allowCoreThreadTimeout=true后,空閑的核心線程超過(guò)存活時(shí)間也會(huì)被回收)。

大于核心線程數(shù)的線程,在空閑時(shí)間超過(guò)keepAliveTime后會(huì)被回收。

線程池剛創(chuàng)建時(shí),里面沒有一個(gè)線程,當(dāng)調(diào)用 execute() 方法添加一個(gè)任務(wù)時(shí),如果正在運(yùn)行的線程數(shù)量小于

corePoolSize,則馬上創(chuàng)建新線程并運(yùn)行這個(gè)任務(wù)。

maximumPoolSize:最大線程數(shù)

線程池允許創(chuàng)建的最大線程數(shù)量。

當(dāng)添加一個(gè)任務(wù)時(shí),核心線程數(shù)已滿,線程池還沒達(dá)到最大線程數(shù),并且沒有空閑線程,工作隊(duì)列已滿的情況下,創(chuàng)建一個(gè)新線程,然后從工作隊(duì)列的頭部取出一個(gè)任務(wù)交由新線程來(lái)處理,而將剛提交的任務(wù)放入工作隊(duì)列尾部。

keepAliveTime:空閑線程存活時(shí)間

當(dāng)一個(gè)可被回收的線程的空閑時(shí)間大于keepAliveTime,就會(huì)被回收。

可被回收的線程:

  1. 設(shè)置allowCoreThreadTimeout=true的核心線程。
  2. 大于核心線程數(shù)的線程(非核心線程)。

unit:時(shí)間單位

keepAliveTime的時(shí)間單位:

TimeUnit.NANOSECONDS
TimeUnit.MICROSECONDS
TimeUnit.MILLISECONDS // 毫秒
TimeUnit.SECONDS
TimeUnit.MINUTES
TimeUnit.HOURS
TimeUnit.DAYS

workQueue:工作隊(duì)列

新任務(wù)被提交后,會(huì)先添加到工作隊(duì)列,任務(wù)調(diào)度時(shí)再?gòu)年?duì)列中取出任務(wù)。工作隊(duì)列實(shí)現(xiàn)了BlockingQueue接口。

JDK默認(rèn)的工作隊(duì)列有五種:

  1. ArrayBlockingQueue 數(shù)組型阻塞隊(duì)列:數(shù)組結(jié)構(gòu),初始化時(shí)傳入大小,有界,F(xiàn)IFO,使用一個(gè)重入鎖,默認(rèn)使用非公平鎖,入隊(duì)和出隊(duì)共用一個(gè)鎖,互斥。
  2. LinkedBlockingQueue 鏈表型阻塞隊(duì)列:鏈表結(jié)構(gòu),默認(rèn)初始化大小為Integer.MAX_VALUE,有界(近似無(wú)解),F(xiàn)IFO,使用兩個(gè)重入鎖分別控制元素的入隊(duì)和出隊(duì),用Condition進(jìn)行線程間的喚醒和等待。
  3. SynchronousQueue 同步隊(duì)列:容量為0,添加任務(wù)必須等待取出任務(wù),這個(gè)隊(duì)列相當(dāng)于通道,不存儲(chǔ)元素。
  4. PriorityBlockingQueue 優(yōu)先阻塞隊(duì)列:無(wú)界,默認(rèn)采用元素自然順序升序排列。
  5. DelayQueue 延時(shí)隊(duì)列:無(wú)界,元素有過(guò)期時(shí)間,過(guò)期的元素才能被取出。

threadFactory:線程工廠

創(chuàng)建線程的工廠,可以設(shè)定線程名、線程編號(hào)等。

12、有了解過(guò)JVM嗎?

沒有了解過(guò)JVM的,這道題是吱吱嗚嗚的,瞎說(shuō)一通,然后尷尬的結(jié)束。

JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫 ,JVM在執(zhí)行Java程序時(shí),會(huì)把它管理的內(nèi)存劃分為若干個(gè)的區(qū)域,每個(gè)區(qū)域都有自己的用途和創(chuàng)建銷毀時(shí)間。如下圖所示,可以分為兩大部分,線程私有區(qū)和共享區(qū)。

下圖是根據(jù)自己理解畫的一個(gè)JVM內(nèi)存模型架構(gòu)圖:

JVM內(nèi)存分為線程私有區(qū)和線程共享區(qū)。

線程私有區(qū)

1、程序計(jì)數(shù)器

當(dāng)同時(shí)進(jìn)行的線程數(shù)超過(guò)CPU數(shù)或其內(nèi)核數(shù)時(shí),就要通過(guò)時(shí)間片輪詢分派CPU的時(shí)間資源,不免發(fā)生線程切換。這時(shí),每個(gè)線程就需要一個(gè)屬于自己的計(jì)數(shù)器來(lái)記錄下一條要運(yùn)行的指令。如果執(zhí)行的是JAVA方法,計(jì)數(shù)器記錄正在執(zhí)行的java字節(jié)碼地址,如果執(zhí)行的是native方法,則計(jì)數(shù)器為空。

2、虛擬機(jī)棧

線程私有的,與線程在同一時(shí)間創(chuàng)建。管理JAVA方法執(zhí)行的內(nèi)存模型。每個(gè)方法執(zhí)行時(shí)都會(huì)創(chuàng)建一個(gè)楨棧來(lái)存儲(chǔ)方法的的變量表、操作數(shù)棧、動(dòng)態(tài)鏈接方法、返回值、返回地址等信息。棧的大小決定了方法調(diào)用的可達(dá)深度(遞歸多少層次,或嵌套調(diào)用多少層其他方法,-Xss參數(shù)可以設(shè)置虛擬機(jī)棧大?。瑮5拇笮】梢允枪潭ㄒ部梢詣?dòng)態(tài)擴(kuò)展。

如果請(qǐng)求的棧深度大于最大可用深度,則拋出stackOverflowError;

如果棧是可動(dòng)態(tài)擴(kuò)展的,但沒有內(nèi)存空間支持?jǐn)U展,則拋出OutofMemoryError。

使用jclasslib工具可以查看class類文件的結(jié)構(gòu)。下圖為棧幀結(jié)構(gòu)圖:


一個(gè)線程對(duì)應(yīng)一個(gè)虛擬機(jī)棧,一個(gè)虛擬機(jī)棧對(duì)應(yīng)多個(gè)棧幀,每個(gè)棧幀的的入棧和出棧表示一個(gè)方法的調(diào)用。

3、本地方法棧

與虛擬機(jī)棧作用相似。但它不是為Java方法服務(wù)的,而是本地方法(C語(yǔ)言)。由于規(guī)范對(duì)這塊沒有強(qiáng)制要求,不同虛擬機(jī)實(shí)現(xiàn)方法不同。

線程共享區(qū)

1、方法區(qū)

線程共享的,用于存放被虛擬機(jī)加載的類的元數(shù)據(jù)信息,如常量、靜態(tài)變量和即時(shí)編譯器編譯后的代碼。若要分代,算是永久代(老年代),以前類大多“static”的,很少被卸載或收集,現(xiàn)回收廢棄常量和無(wú)用的類。其中運(yùn)行時(shí)常量池存放編譯生成的各種常量。(如果hotspot虛擬機(jī)確定一個(gè)類的定義信息不會(huì)被使用,也會(huì)將其回收?;厥盏幕緱l件至少有:所有該類的實(shí)例被回收,而且裝載該類的ClassLoader被回收)。

2、堆

存放對(duì)象實(shí)例和數(shù)組,是垃圾回收的主要區(qū)域,分為新生代和老年代。剛創(chuàng)建的對(duì)象在新生代的Eden區(qū)中,經(jīng)過(guò)GC后進(jìn)入新生代的S0區(qū)中,再經(jīng)過(guò)GC進(jìn)入新生代的S1區(qū)中,15次GC后仍存在就進(jìn)入老年代。這是按照一種回收機(jī)制進(jìn)行劃分的,不是固定的。若堆的空間不夠?qū)嵗峙?,則OutOfMemoryError

13、類加載機(jī)制是什么?

更偏向于類裝載,因?yàn)樵诿枋鲞@個(gè)過(guò)程的時(shí)候。,第一步就是加載,這樣更容易辨別。

JVM類加載分為5個(gè)過(guò)程:加載,驗(yàn)證,準(zhǔn)備,解析,初始化,使用,卸載,如下圖所示:

下面來(lái)看看加載,驗(yàn)證,準(zhǔn)備,解析,初始化這5個(gè)過(guò)程的具體動(dòng)作。

加載

加載主要是將.class文件(并不一定是.class??梢允荶IP包,網(wǎng)絡(luò)中獲取)中的二進(jìn)制字節(jié)流讀入到JVM中。在加載階段,JVM需要完成3件事:1)通過(guò)類的全限定名獲取該類的二進(jìn)制字節(jié)流;2)將字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu);3)在內(nèi)存中生成一個(gè)該類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口。

連接

驗(yàn)證

驗(yàn)證是連接階段的第一步,主要確保加載進(jìn)來(lái)的字節(jié)流符合JVM規(guī)范。驗(yàn)證階段會(huì)完成以下4個(gè)階段的檢驗(yàn)動(dòng)作:1)文件格式驗(yàn)證 2)元數(shù)據(jù)驗(yàn)證(是否符合Java語(yǔ)言規(guī)范) 3)字節(jié)碼驗(yàn)證(確定程序語(yǔ)義合法,符合邏輯) 4)符號(hào)引用驗(yàn)證(確保下一步的解析能正常執(zhí)行)

準(zhǔn)備

主要為靜態(tài)變量在方法區(qū)分配內(nèi)存,并設(shè)置默認(rèn)初始值。

解析

是虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用的過(guò)程。

初始化

初始化階段是類加載過(guò)程的最后一步,主要是根據(jù)程序中的賦值語(yǔ)句主動(dòng)為類變量賦值。注:1)當(dāng)有父類且父類為初始化的時(shí)候,先去初始化父類;2)再進(jìn)行子類初始化語(yǔ)句。

14、垃圾回收算法有哪些?

我見過(guò)有的面試官,上來(lái)啥也不問,直接問垃圾回收算法有哪些,隨便看看,學(xué)學(xué)就知道了。

GC最基礎(chǔ)的算法有三種:標(biāo)記 -清除算法、復(fù)制算法、標(biāo)記-壓縮算法。

我們常用的垃圾回收器一般都采用分代收集算法,然后針對(duì)不同的代進(jìn)行使用不同的算法。

  • 標(biāo)記 -清除算法,“標(biāo)記-清除”(Mark-Sweep)算法,如它的名字一樣,算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象。
  • 復(fù)制算法,“復(fù)制”(Copying)的收集算法,它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另外一塊上面,然后再把已使用過(guò)的內(nèi)存空間一次清理掉。
  • 標(biāo)記-壓縮算法,標(biāo)記過(guò)程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存
  • 分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分為新生代和老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/section>

15、熟悉哪些JVM調(diào)優(yōu)參數(shù)?

不能說(shuō)我們要把下面的全部背下來(lái),但是最起碼的堆相關(guān)參數(shù)的應(yīng)該有所知道,肯定知道的越多越好。

「堆棧內(nèi)存相關(guān)」

  • -Xms 設(shè)置初始堆的大小
  • -Xmx 設(shè)置最大堆的大小
  • -Xmn 設(shè)置年輕代大小,相當(dāng)于同時(shí)配置-XX:NewSize和-XX:MaxNewSize為一樣的值
  • -Xss  每個(gè)線程的堆棧大小
  • -XX:NewSize 設(shè)置年輕代大小(for 1.3/1.4)
  • -XX:MaxNewSize 年輕代最大值(for 1.3/1.4)
  • -XX:NewRatio 年輕代與年老代的比值(除去持久代)
  • -XX:SurvivorRatio Eden區(qū)與Survivor區(qū)的的比值
  • -XX:PretenureSizeThreshold 當(dāng)創(chuàng)建的對(duì)象超過(guò)指定大小時(shí),直接把對(duì)象分配在老年代。
  • -XX:MaxTenuringThreshold設(shè)定對(duì)象在Survivor復(fù)制的最大年齡閾值,超過(guò)閾值轉(zhuǎn)移到老年代

「垃圾收集器相關(guān)」

-XX:+UseParallelGC:選擇垃圾收集器為并行收集器。

  • -XX:ParallelGCThreads=20:配置并行收集器的線程數(shù)
  • -XX:+UseConcMarkSweepGC:設(shè)置年老代為并發(fā)收集。
  • -XX:CMSFullGCsBeforeCompaction=5 由于并發(fā)收集器不對(duì)內(nèi)存空間進(jìn)行壓縮、整理,所以運(yùn)行一段時(shí)間以后會(huì)產(chǎn)生“碎片”,使得運(yùn)行效率降低。此值設(shè)置運(yùn)行5次GC以后對(duì)內(nèi)存空間進(jìn)行壓縮、整理。
  • -XX:+UseCMSCompactAtFullCollection:打開對(duì)年老代的壓縮??赡軙?huì)影響性能,但是可以消除碎片

「輔助信息相關(guān)」

  • -XX:+PrintGCDetails 打印GC詳細(xì)信息
  • -XX:+HeapDumpOnOutOfMemoryError讓JVM在發(fā)生內(nèi)存溢出的時(shí)候自動(dòng)生成內(nèi)存快照,排查問題用
  • -XX:+DisableExplicitGC禁止系統(tǒng)System.gc(),防止手動(dòng)誤觸發(fā)FGC造成問題.
  • -XX:+PrintTLAB 查看TLAB空間的使用情況

16、熟悉分布式鎖嗎?有哪些實(shí)現(xiàn)方案?

變向的喜歡問,你們項(xiàng)目中有用到分布式鎖嗎?是怎么使用的,為什么這么使用?這么使用會(huì)不會(huì)有問題,有什么問題,遇到這問題了怎么解決?

項(xiàng)目中有用到分布式鎖,使用Zookeeper的實(shí)現(xiàn)方案。

分布式鎖實(shí)現(xiàn)方案,常見有如下幾種:

  • 基于表主鍵唯一做分布式鎖
  • 通過(guò)數(shù)據(jù)庫(kù)mvcc實(shí)現(xiàn)樂觀鎖
  • 基于Redis實(shí)現(xiàn)分布式鎖
  • 基于Redlock算法實(shí)現(xiàn)分布式鎖
  • 基于Zookeeper實(shí)現(xiàn)分布式鎖

17、哪一種方案是最好的?

沒有最好的方案,只有相對(duì)最優(yōu)方案。

1、Redis的分布式鎖中redisson一般為單實(shí)例,當(dāng)單實(shí)例不可用時(shí),會(huì)阻塞業(yè)務(wù)流程。主從方式、主從數(shù)據(jù)異步,會(huì)存在鎖失效的問題。RedLock一般要求至少3臺(tái)以上的redis主從實(shí)例,維護(hù)成本相對(duì)來(lái)說(shuō)比較高。

2、ZK鎖具備高可用、可重入、阻塞鎖特性,可解決失效死鎖問題。但是因?yàn)樾枰l繁的創(chuàng)建和刪除節(jié)點(diǎn),性能上不如Redis方式。

3、 ETCD分布式鎖的實(shí)現(xiàn)原理與zk鎖類似,但是ETCD分布式鎖更加可靠強(qiáng)大。其Lease功能保證分布式鎖的安全性;watch功能支持監(jiān)聽某個(gè)固定的key,也支持watch一個(gè)范圍的key(前綴機(jī)制);revision功能可通過(guò) Revision 的大小就可以知道進(jìn)行寫操作的順序。可以避免 “羊群效應(yīng)” (也稱 “驚群效應(yīng)”),實(shí)現(xiàn)公平鎖。前綴機(jī)制與watch功能配合使用解決了死鎖問題??傊?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">ETCD的靈感來(lái)源于Zookeeper,但實(shí)現(xiàn)的時(shí)候做了很多的改進(jìn),如:高負(fù)載下的穩(wěn)定讀寫、數(shù)據(jù)模型的多版本并發(fā)控制、穩(wěn)定的watch功能,通知訂閱者監(jiān)聽值得變化、可以容忍腦裂現(xiàn)場(chǎng)的發(fā)生、客戶端的協(xié)議使用gRPC協(xié)議,支持go、c++、Java等。

4、數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖,對(duì)數(shù)據(jù)庫(kù)表侵入較大,每個(gè)表需要增加version等字段,高并發(fā)下存在很多更新失敗。數(shù)據(jù)庫(kù)寫入是磁盤io,性能方面差一些。數(shù)據(jù)庫(kù)能支持的最大QPS也有限制,很難滿足高并發(fā)的需要。

總結(jié)

小伙伴本次面試中發(fā)揮的還是挺好的,最終收獲offer,恭喜這位朋友。

最后,希望大家平時(shí)就算不面試,也要為日后的面試做準(zhǔn)備,做一個(gè)能進(jìn)能退的人。

機(jī)會(huì)總是留給有準(zhǔn)備的人,加油!

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    国产不卡最新在线视频| 国产精品一区二区视频成人| 国产成人免费高潮激情电| 欧美日韩国产免费看黄片| 色哟哟国产精品免费视频| 欧美日韩国产另类一区二区| 国产亚洲不卡一区二区| 五月婷婷缴情七月丁香| 黄色av尤物白丝在线播放网址| 日韩av生活片一区二区三区| 狠狠干狠狠操在线播放| 国产传媒高清视频在线| 国产真人无遮挡免费视频一区| 国产精品不卡一区二区三区四区| 日韩高清中文字幕亚洲| 91后入中出内射在线| 黑丝国产精品一区二区| 国产麻豆成人精品区在线观看| 日本不卡片一区二区三区| 美女激情免费在线观看| 国产在线成人免费高清观看av| 国产色偷丝袜麻豆亚洲| 夫妻性生活一级黄色录像| 99久久精品午夜一区二| 国产在线小视频你懂的| 欧美日韩精品视频在线| 国产午夜精品福利免费不| 欧美多人疯狂性战派对| 亚洲国产另类久久精品| 日本加勒比系列在线播放| 果冻传媒在线观看免费高清| 女同伦理国产精品久久久| 日本女优一区二区三区免费 | 亚洲中文字幕日韩在线| 国产在线一区中文字幕| 日韩欧美综合在线播放| 不卡一区二区高清视频| 精品香蕉国产一区二区三区| 老司机精品一区二区三区| 人妻精品一区二区三区视频免精| 国产原创激情一区二区三区|