做網(wǎng)店在素材網(wǎng)站找的圖侵權(quán)嗎地域名網(wǎng)址查詢
努力了那么多年,回頭一望,幾乎全是漫長(zhǎng)的挫折和煎熬。對(duì)于大多數(shù)人的一生來(lái)說,順風(fēng)順?biāo)皇桥紶?挫折、不堪、焦慮和迷茫才是主旋律。我們登上并非我們所選擇的舞臺(tái),演出并非我們所選擇的劇本。繼續(xù)加油吧!
目錄
1.ArrayList與LinkedList區(qū)別, 應(yīng)用場(chǎng)景?
2.LinkedList是單鏈表還是雙鏈表?Linkedlist查找第二個(gè)和倒數(shù)第二個(gè)效率一樣嗎?為什么?
3.線程啟動(dòng)方式有哪些,區(qū)別是什么?
4.?除了加鎖如何實(shí)現(xiàn)線程安全 ?
5.volatile加鎖么?什么是自旋?什么是CPU空轉(zhuǎn)?
6.進(jìn)程和線程區(qū)別??通信過程中區(qū)別?
7.Java的基本數(shù)據(jù)類型?常量池有哪些?在哪里?
8.TreadLoacl解釋一下,為什么會(huì)內(nèi)存泄露?如何避免內(nèi)存泄漏?
9.HashMap和hashTable的區(qū)別?
10.HashMap得擴(kuò)容機(jī)制?
11.ArrayList得擴(kuò)容機(jī)制?
12.spring如何開開啟一個(gè)事務(wù)?
13.BIO,NIO,AIO模型解釋一下?NIO的三大組件?
14.講一下聚簇索引和非聚簇索引區(qū)別?
15.如何開啟一個(gè)線程?調(diào)用start和run方法有什么不同?
16.InnodB和myiasm的區(qū)別?
17.數(shù)據(jù)庫(kù)隔離級(jí)別?MySQL和Oracle默認(rèn)得隔離級(jí)別?
18.AOP解釋一下?
19.CAS介紹一下?Syschronized?ReentrantLock?Lock?Synchronized鎖升級(jí)?
20.算法題:二叉樹的先序,中序,后序遍歷
1.ArrayList與LinkedList區(qū)別, 應(yīng)用場(chǎng)景?
通常情況下,ArrayList和LinkedList的區(qū)別有以下幾點(diǎn):
1. 數(shù)據(jù)結(jié)構(gòu):ArrayList是實(shí)現(xiàn)了基于動(dòng)態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu),而LinkedList是基于鏈表的數(shù)據(jù)結(jié)構(gòu); ?2. 隨機(jī)訪問:對(duì)于隨機(jī)訪問get和set,ArrayList要優(yōu)于LinkedList,因?yàn)長(zhǎng)inkedList要移動(dòng)指針;
?3. 添加刪除操作:對(duì)于添加和刪除操作add和remove,一般大家都會(huì)說LinkedList要比ArrayList快,因?yàn)锳rrayList要移動(dòng)數(shù)據(jù)。但是實(shí)際情況并非這樣,對(duì)于添加或刪除,LinkedList和ArrayList并不能明確說明誰(shuí)快誰(shuí)慢,當(dāng)數(shù)據(jù)量較大時(shí),大約在容量的1/10處開始,LinkedList的效率就開始沒有ArrayList效率高了,特別到一半以及后半的位置插入時(shí),LinkedList效率明顯要低于ArrayList,而且數(shù)據(jù)量越大,越明顯;
應(yīng)用場(chǎng)景:
(1)如果應(yīng)用程序?qū)?shù)據(jù)有較多的隨機(jī)訪問,ArrayList對(duì)象要優(yōu)于LinkedList對(duì)象;
? ( 2 ) 如果應(yīng)用程序有更多的插入或者刪除操作,較少的隨機(jī)訪問,LinkedList對(duì)象要優(yōu)于ArrayList對(duì)象;
(3)不過ArrayList的插入,刪除操作也不一定比LinkedList慢。
2.LinkedList是單鏈表還是雙鏈表?Linkedlist查找第二個(gè)和倒數(shù)第二個(gè)效率一樣嗎?為什么?
Linkedlist,雙向鏈表,優(yōu)點(diǎn),增加刪除,用時(shí)間很短,但是因?yàn)闆]有索引,對(duì)索引的操作,比較麻煩,只能循環(huán)遍歷,但是每次循環(huán)的時(shí)候,都會(huì)先判斷一下,這個(gè)索引位于鏈表的前部分還是后部分,每次都會(huì)遍歷鏈表的一半 ,而不是全部遍歷。?
雙向鏈表,都有一個(gè)previous和next, 鏈表最開始的部分都有一個(gè)fiest和last 指向第一個(gè)元素,和最后一個(gè)元素。增加和刪除的時(shí)候,只需要更改一個(gè)previous和next,就可以實(shí)現(xiàn)增加和刪除,所以說,LinkedList對(duì)于數(shù)據(jù)的刪除和增加相當(dāng)?shù)姆奖恪?
按道理來(lái)說,Linkedlist查找第二個(gè)和倒數(shù)第二個(gè)效率一樣,因?yàn)殡p向鏈表可以兩個(gè)方向遍歷。
3.線程啟動(dòng)方式有哪些,區(qū)別是什么?
1.繼承Thread類,并復(fù)寫run方法,創(chuàng)建該類對(duì)象,調(diào)用start方法開啟線程。
? 2.實(shí)現(xiàn)Runnable接口,復(fù)寫run方法,創(chuàng)建Thread類對(duì)象,將Runnable子類對(duì)象傳遞給Thread類對(duì)象。調(diào)用start方法開啟線程。
? 3.創(chuàng)建FutureTask對(duì)象,創(chuàng)建Callable子類對(duì)象,復(fù)寫call(相當(dāng)于run)方法,將其傳遞給FutureTask對(duì)象(相當(dāng)于一個(gè)Runnable)。
? 創(chuàng)建Thread類對(duì)象,將FutureTask對(duì)象傳遞給Thread對(duì)象。調(diào)用start方法開啟線程。這種方式可以獲得線程執(zhí)行完之后的返回值。
?
4.?除了加鎖如何實(shí)現(xiàn)線程安全 ?
使用線程安全的類,本質(zhì)上還是加鎖。
在性能和安全性之前取得一個(gè)平衡,所以引出一個(gè)無(wú)鎖并發(fā)的概念,當(dāng)然,本質(zhì)上來(lái)講,就是扯犢子,保持原子性肯定是需要加鎖的。
第一種方法,通過自旋鎖,線程在沒有搶占鎖的情況下,先自旋指定的次數(shù)去獲得鎖。
第二種方法是樂觀鎖,給每個(gè)數(shù)據(jù)增加一個(gè)版本號(hào),一旦數(shù)據(jù)發(fā)生變化,則去修改這個(gè)版本號(hào)。
第三種就是盡量在業(yè)務(wù)中減少共享對(duì)象的使用,實(shí)現(xiàn)隔離減少并發(fā)。
第四種就是使用Threadlocal創(chuàng)建共享變量的副本。
5.volatile加鎖么?什么是自旋?什么是CPU空轉(zhuǎn)?
volatile不需要加鎖,比synchronized更輕便,不會(huì)阻塞線程,volatile能保證可見性和有序性但不能保證多線程下的原子性。volatile只能保證受限的原子性。通過對(duì)volatile修飾的變量的讀寫操作前后加上各種特定的內(nèi)存屏障來(lái)禁止指令重排序來(lái)保證有序性。可見性:變量被volatile修飾,java內(nèi)存模型能確保所有的線程看到的這個(gè)變量的值是一致的。
自旋:就是自己在這里不停地循環(huán),直到目標(biāo)達(dá)成,CAS算法就是一種自旋鎖機(jī)制,不會(huì)導(dǎo)致線程阻塞,獲取不到鎖,就不停地自旋嘗試加鎖。
cpu空轉(zhuǎn): 如果CAS失敗,會(huì)一直進(jìn)行嘗試,如果CAS長(zhǎng)時(shí)間一直不成功,不釋放CPU,可能會(huì)給CPU帶來(lái)很大的開銷。(CPU空轉(zhuǎn)問題)(鎖饑餓)
6.進(jìn)程和線程區(qū)別??通信過程中區(qū)別?
進(jìn)程與線程的區(qū)別總結(jié):
本質(zhì)區(qū)別:進(jìn)程是操作系統(tǒng)資源分配的基本單位,而線程是處理器任務(wù)調(diào)度和執(zhí)行的基本單位。
包含關(guān)系:一個(gè)進(jìn)程至少有一個(gè)線程,線程是進(jìn)程的一部分,所以線程也被稱為輕權(quán)進(jìn)程或者輕量級(jí)進(jìn)程。
資源開銷:每個(gè)進(jìn)程都有獨(dú)立的地址空間,進(jìn)程之間的切換會(huì)有較大的開銷;線程可以看做輕量級(jí)的進(jìn)程,同一個(gè)進(jìn)程內(nèi)的線程共享進(jìn)程的地址空間,每個(gè)線程都有自己獨(dú)立的運(yùn)行棧和程序計(jì)數(shù)器,線程之間切換的開銷小。
影響關(guān)系:一個(gè)進(jìn)程崩潰后,在保護(hù)模式下其他進(jìn)程不會(huì)被影響,但是一個(gè)線程崩潰可能導(dǎo)致整個(gè)進(jìn)程被操作系統(tǒng)殺掉,所以多進(jìn)程要比多線程健壯。
?
進(jìn)程間通信:
①管道
管道傳輸數(shù)據(jù)是單向的,如果想相互通信,我們需要?jiǎng)?chuàng)建兩個(gè)管道才行,半雙工。
②消息隊(duì)列:
基本原理:A 進(jìn)程要給 B 進(jìn)程發(fā)送消息,A 進(jìn)程把數(shù)據(jù)放在對(duì)應(yīng)的消息隊(duì)列后就可以正常返回了,B 進(jìn)程需要的時(shí)候再去讀取數(shù)據(jù)就可以了。
③共享內(nèi)存:
共享內(nèi)存解決了消息隊(duì)列的讀取和寫?的過程會(huì)有發(fā)生用戶態(tài)與內(nèi)核態(tài)之間的消息拷貝的問題。
就是拿出?塊虛擬地址空間來(lái),映射到相同的物理內(nèi)存中。這段共享內(nèi)存由一個(gè)進(jìn)程創(chuàng)建,但多個(gè)進(jìn)程都可以訪問。這樣這個(gè)進(jìn)程寫?的東?,另外?個(gè)進(jìn)程馬上就能看到了,都不需要拷貝,提高了進(jìn)程間通信的速度。
④信號(hào)量:
- 防止多進(jìn)程競(jìng)爭(zhēng)共享資源,造成的數(shù)據(jù)錯(cuò)亂,所以需要保護(hù)機(jī)制,使得共享的資源,在任意時(shí)刻只能被?個(gè)進(jìn)程訪問,信號(hào)量就實(shí)現(xiàn)了這?保護(hù)機(jī)制。
- 信號(hào)量其實(shí)是?個(gè)整型的計(jì)數(shù)器,主要用于實(shí)現(xiàn)進(jìn)程間的互斥與同步,不是用于緩存進(jìn)程間通信的數(shù)據(jù)。
⑤Socket:
那要想跨網(wǎng)絡(luò)與不同主機(jī)上的進(jìn)程之間通信,就需要 Socket 通信了。還可以在同主機(jī)上進(jìn)程間通信。TCP和UDP。
線程間通信:
線程間的通信目的主要是用于線程同步。所以線程沒有像進(jìn)程通信中的用于數(shù)據(jù)交換的通信機(jī)制。
同一進(jìn)程的不同線程共享同一份內(nèi)存區(qū)域,所以線程之間可以方便、快速地共享信息。只需要將數(shù)據(jù)復(fù)制到共享(全局或堆)變量中即可。但是需要避免出現(xiàn)多個(gè)線程試圖同時(shí)修改同一份信息。
1、互斥鎖
在訪問共享資源前進(jìn)行加鎖,在訪問完成后釋放互斥鎖。加鎖后其他想訪問此資源的線程會(huì)進(jìn)入阻塞,直到當(dāng)前線程釋放互斥鎖。注意防止死鎖。
2、讀寫鎖
一次只有一個(gè)線程可以占有寫模式的讀寫鎖,但是多個(gè)線程可以同時(shí)占有讀模式的讀寫鎖。
當(dāng)讀寫鎖是寫加鎖狀態(tài)時(shí),在這個(gè)鎖被解鎖之前,所有試圖對(duì)這個(gè)鎖加鎖的線程都會(huì)被阻塞。
當(dāng)讀寫鎖在讀加鎖狀態(tài)時(shí),所有試圖以讀模式對(duì)它進(jìn)行加鎖的線程都可以得到訪問權(quán),但是任何希望以寫模式對(duì)此鎖進(jìn)行加鎖的線程都會(huì)阻塞,直到所有的線程釋放它們的讀鎖為止。
3、條件變量
互斥量用于上鎖,條件變量則用于等待,并且條件變量總是需要與互斥鎖一起使用,運(yùn)行線程以無(wú)競(jìng)爭(zhēng)的方式等待特定的條件發(fā)生。
條件變量本身是由互斥量保護(hù)的,線程在改變條件變量之前必須首先加互斥鎖。(某共享數(shù)據(jù)達(dá)到某值的時(shí)候,喚醒等待這個(gè)共享數(shù)據(jù)的線程)
4、信號(hào)量
使用線程的信號(hào)量可以高效地完成基于線程的資源計(jì)數(shù)。信號(hào)量實(shí)際上是一個(gè)非負(fù)的整數(shù)計(jì)數(shù)器,用來(lái)實(shí)現(xiàn)對(duì)公共資源的控制。
在公共資源增加的時(shí)候,信號(hào)量就增加;公共資源減少的時(shí)候,信號(hào)量就減少;只有當(dāng)信號(hào)量的值大于0的時(shí)候,才能訪問信號(hào)量所代表的公共資源。
?
7.Java的基本數(shù)據(jù)類型?常量池有哪些?在哪里?
Java中有8種基本數(shù)據(jù)類型,分別為:
6種數(shù)字類型 :byte、short、int、long、float、double
1種字符類型:char
1種布爾型:boolean
字符串常量池(String Constant Pool)
class常量池(Class Constant Pool)
運(yùn)行時(shí)常量池(Runtime Constant Pool)
Java6及之前,常量池是存放在方法區(qū)(永久代)中的。
Java7,將常量池是存放到了堆中。
Java8之后,取消了整個(gè)永久代區(qū)域,取而代之的是元空間。運(yùn)行時(shí)常量池和靜態(tài)常量池存放在元空間中,而字符串常量池依然存放在堆中。
?
8.TreadLoacl解釋一下,為什么會(huì)內(nèi)存泄露?如何避免內(nèi)存泄漏?
多個(gè)線程訪問同一個(gè)共享變量時(shí),如果不做同步控制,往往會(huì)出現(xiàn)「數(shù)據(jù)不一致」的問題,通常會(huì)使用synchronized關(guān)鍵字加鎖來(lái)解決,ThreadLocal則換了一個(gè)思路。
ThreadLocal本身并不存儲(chǔ)值,它依賴于Thread類中的ThreadLocalMap,當(dāng)調(diào)用set(T value)時(shí),ThreadLocal將自身作為Key,值作為Value存儲(chǔ)到Thread類中的ThreadLocalMap中,這就相當(dāng)于所有線程讀寫的都是自身的一個(gè)私有副本,線程之間的數(shù)據(jù)是隔離的,互不影響,也就不存在線程安全問題了。
Entry將ThreadLocal作為Key,值作為value保存,它繼承自WeakReference,注意構(gòu)造函數(shù)里的第一行代碼super(k),這意味著ThreadLocal對(duì)象是一個(gè)「弱引用」。綜上所述,由于ThreadLocal對(duì)象是弱引用,如果外部沒有強(qiáng)引用指向它,它就會(huì)被GC回收,導(dǎo)致Entry的Key為null,如果這時(shí)value外部也沒有強(qiáng)引用指向它,那么value就永遠(yuǎn)也訪問不到了,按理也應(yīng)該被GC回收,但是由于Entry對(duì)象還在強(qiáng)引用value,導(dǎo)致value無(wú)法被回收,這時(shí)「內(nèi)存泄漏」就發(fā)生了,value成了一個(gè)永遠(yuǎn)也無(wú)法被訪問,但是又無(wú)法被回收的對(duì)象。
如何避免內(nèi)存泄漏?
使用ThreadLocal時(shí),一般建議將其聲明為static final的,避免頻繁創(chuàng)建ThreadLocal實(shí)例。
盡量避免存儲(chǔ)大對(duì)象,如果非要存,那么盡量在訪問完成后及時(shí)調(diào)用remove()刪除掉。
9.HashMap和hashTable的區(qū)別?
1.HashMap是Hashtable的輕量級(jí)實(shí)現(xiàn)(非線程安全的實(shí)現(xiàn)),他們都完成了Map接口,主要區(qū)別在于HashMap允許空(null)鍵值(key),由于非線程安全,在只有一個(gè)線程訪問的情況下,效率要高于Hashtable。
2.HashMap允許將null作為一個(gè)entry的key或者value,而Hashtable不允許。
HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因?yàn)閏ontains方法容易讓人引起誤解。
3.Hashtable繼承自Dictionary類,而HashMap是Java1.2引進(jìn)的Map interface的一個(gè)實(shí)現(xiàn)。
4.最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多個(gè)線程訪問Hashtable時(shí),不需要自己為它的方法實(shí)現(xiàn)同步,而HashMap就必須為之提供同步。
?
10.HashMap得擴(kuò)容機(jī)制?
HashMap的底層有數(shù)組 + 鏈表(紅黑樹)組成,數(shù)組的大小可以在構(gòu)造方法時(shí)設(shè)置,默認(rèn)大小為16,數(shù)組中每一個(gè)元素就是一個(gè)鏈表,jdk7之前鏈表中的元素采用頭插法插入元素,jdk8之后采用尾插法插入元素,由于插入的元素越來(lái)越多,查找效率就變低了,所以滿足某種條件時(shí),鏈表會(huì)轉(zhuǎn)換成紅黑樹。隨著元素的增加,HashMap的數(shù)組會(huì)頻繁擴(kuò)容,如果構(gòu)造時(shí)不賦予加載因子默認(rèn)值,那么負(fù)載因子默認(rèn)值為0.75,數(shù)組擴(kuò)容的情況如下:
1:當(dāng)添加某個(gè)元素后,數(shù)組的總的添加元素?cái)?shù)大于了 數(shù)組長(zhǎng)度 * 0.75(默認(rèn),也可自己設(shè)定),數(shù)組長(zhǎng)度擴(kuò)容為兩倍。(如開始創(chuàng)建HashMap集合后,數(shù)組長(zhǎng)度為16,臨界值為16 * 0.75 = 12,當(dāng)加入元素后元素個(gè)數(shù)超過12,數(shù)組長(zhǎng)度擴(kuò)容為32,臨界值變?yōu)?4)
2:在沒有紅黑樹的條件下,添加元素后數(shù)組中某個(gè)鏈表的長(zhǎng)度超過了8,數(shù)組會(huì)擴(kuò)容為兩倍.(如開始創(chuàng)建HashMAp集合后,假設(shè)添加的元素都在一個(gè)鏈表中,當(dāng)鏈表中元素為8時(shí),再在鏈表中添加一個(gè)元素,此時(shí)若數(shù)組中不存在紅黑樹,則數(shù)組會(huì)擴(kuò)容為兩倍變成32,假設(shè)此時(shí)鏈表元素排列不變,再在該鏈表中添加一個(gè)元素,數(shù)組長(zhǎng)度再擴(kuò)容兩倍,變?yōu)?4,假設(shè)此時(shí)鏈表元素排列還是不變,則此時(shí)鏈表中存在10個(gè)元素,這是HashMap鏈表元素?cái)?shù)存在的最大值,此時(shí),再加入元素,滿足了鏈表樹化的兩個(gè)條件(1:數(shù)組長(zhǎng)度達(dá)到64, 2:該鏈表長(zhǎng)度達(dá)到了8),該鏈表會(huì)轉(zhuǎn)換為紅黑樹
?
11.ArrayList得擴(kuò)容機(jī)制?
ArrayList的底層是一個(gè)動(dòng)態(tài)數(shù)組,ArrayList首先會(huì)對(duì)傳進(jìn)來(lái)的初始化參數(shù)initalCapacity進(jìn)行判斷
- 如果參數(shù)等于0,則將數(shù)組初始化為一個(gè)空數(shù)組,
- 如果不等于0,將數(shù)組初始化為一個(gè)容量為10的數(shù)組。
擴(kuò)容時(shí)機(jī)
當(dāng)數(shù)組的大小大于初始容量的時(shí)候(比如初始為10,當(dāng)添加第11個(gè)元素的時(shí)候),就會(huì)進(jìn)行擴(kuò)容,新的容量為舊的容量的1.5倍。
擴(kuò)容方式
?擴(kuò)容的時(shí)候,會(huì)以新的容量建一個(gè)原數(shù)組的拷貝,修改原數(shù)組,指向這個(gè)新數(shù)組,原數(shù)組被拋棄,會(huì)被GC回收。
12.spring如何開開啟一個(gè)事務(wù)?
注解聲明式事務(wù)
通過注解的方式使用 Spring 事務(wù)管理,首先需要開啟這個(gè)功能,有兩種方式。
- Spring XML 配置文件中配置?
<tx:annotation-driven/>
。 - Spring 配置類上添加?
@EnableTransactionManagement
?注解。
開啟注解后還需要在 Spring 中配置 TransactionManager 作為 bean,通常使用的實(shí)現(xiàn)為 DataSourceTransactionManager,如果引入了 spring-boot-starter-jdbc,則無(wú)需顯式配置 TransactionManager,而只需要配置一個(gè) Datasource 即可。
開啟注解支持后需要在 Spring Bean 的類或方法上使用 @Transactional 注解。
?
13.BIO,NIO,AIO模型解釋一下?NIO的三大組件?
Java共支持3種網(wǎng)絡(luò)編程的I/O模型:BIO、NIO、AIO
BIO:
同步并阻塞(傳統(tǒng)阻塞型),服務(wù)器實(shí)現(xiàn)模式為一個(gè)連接一個(gè)線程,即客戶端有連接請(qǐng)求時(shí)服務(wù)器端就需要啟動(dòng)一個(gè)線程進(jìn)行處理,如果這個(gè)連接不做任何事情會(huì)造成不必要的線程開銷,可以通過線程池機(jī)制改善(實(shí)現(xiàn)多個(gè)客戶連接服務(wù)器)。
BIO編程流程的梳理:
- 服務(wù)器端啟動(dòng)一個(gè)ServerSocket,注冊(cè)端口,調(diào)用accpet方法監(jiān)聽客戶端的Socket連接
- 客戶端啟動(dòng)Socket對(duì)服務(wù)器進(jìn)行通信,默認(rèn)情況下服務(wù)器端需要對(duì)每個(gè)客戶建立一個(gè)線程與之通訊
NIO:
同步非阻塞,服務(wù)器實(shí)現(xiàn)模式為一個(gè)線程處理多個(gè)請(qǐng)求(連接),即客戶端發(fā)送的連接請(qǐng)求都會(huì)注冊(cè)到多路復(fù)用器上,多路復(fù)用器輪詢到連接有I/O請(qǐng)求就進(jìn)行處理。
- NIO有三大核心部分:Channel(通道)、Buffer(緩沖區(qū))、Selector(選擇器)
Java NIO的非阻塞模式,使一個(gè)線程從某通道發(fā)送請(qǐng)求或者讀取數(shù)據(jù),但是它僅能得到目前可用的數(shù)據(jù),如果目前沒有數(shù)據(jù)可用時(shí),就什么都不會(huì)獲取,而不是保持線程阻塞,所以直至數(shù)據(jù)變的可以讀取之前,該線程可以繼續(xù)做其他的事情。非阻塞寫也是如此,一個(gè)線程請(qǐng)求寫入一些數(shù)據(jù)到某通道,但不需要等待它完全寫入,這個(gè)線程同時(shí)可以去做別的事情。
?
AIO:
異步非阻塞,服務(wù)器實(shí)現(xiàn)模式為一個(gè)有效請(qǐng)求一個(gè)線程,客戶端的I/O請(qǐng)求都是由操作系統(tǒng)先完成了再通知服務(wù)器應(yīng)用去啟動(dòng)線程進(jìn)行處理,一般適用于連接數(shù)較多且連接時(shí)間較長(zhǎng)的應(yīng)用。
14.講一下聚簇索引和非聚簇索引區(qū)別?
在 MySQL 默認(rèn)引擎 InnoDB 中,索引大致可分為兩類:聚簇索引和非聚簇索引。
聚簇索引(Clustered Index)一張表中只能有一個(gè),一般指的是主鍵索引(如果存在主鍵索引的話),聚簇索引也被稱之為聚集索引。索引的葉節(jié)點(diǎn)就是數(shù)據(jù)節(jié)點(diǎn)。將索引和表數(shù)據(jù)放到同一個(gè)節(jié)點(diǎn)中,索引結(jié)構(gòu)的葉子節(jié)點(diǎn)存放數(shù)據(jù),找到了索引,即找到了數(shù)據(jù)
非聚簇索引的葉節(jié)點(diǎn)仍然是索引節(jié)點(diǎn),只不過有一個(gè)指針指向?qū)?yīng)的數(shù)據(jù)塊。索引存儲(chǔ)和數(shù)據(jù)存儲(chǔ)分離,索引結(jié)構(gòu)的葉子節(jié)點(diǎn)指向數(shù)據(jù)的位置。通過索引找到位置,再通過位置找到數(shù)據(jù),這個(gè)過程叫做回表查詢。一個(gè)表可以有多個(gè)非聚簇索引。非聚簇索引也成為輔助索引。
聚集索引表數(shù)據(jù)按照索引的順序來(lái)存儲(chǔ)的,也就是說索引項(xiàng)的順序與表中記錄的物理順序一致。
非聚集索引。表數(shù)據(jù)存儲(chǔ)順序與索引順序無(wú)關(guān)。
15.如何開啟一個(gè)線程?調(diào)用start和run方法有什么不同?
1)繼承Thread類,并重寫run()方法
2)實(shí)現(xiàn)Runnable接口并重寫run()方法
3)實(shí)現(xiàn)Callable接口并重寫call()方法
啟動(dòng)一個(gè)線程是調(diào)用start()方法,使線程就緒狀態(tài),以后可以被調(diào)度為運(yùn)行狀態(tài),一個(gè)線程必須關(guān)聯(lián)一些具體的執(zhí)行代碼,run()方法是該線程所關(guān)聯(lián)的執(zhí)行代碼。
16.InnodB和myiasm的區(qū)別?
? ? ? ?1.InnoDB 支持事務(wù),MyISAM 不支持。對(duì)于InnoDB每一條SQL語(yǔ)言都默認(rèn)封裝成事務(wù),自動(dòng)提交,這樣會(huì)影響速度,所以最好把多條SQL語(yǔ)言放在begin和commit之間,組成一個(gè)事務(wù);
??2. InnoDB 支持外鍵,而 MyISAM 不支持。對(duì)一個(gè)包含外鍵的InnoDB表轉(zhuǎn)為MYISAM會(huì)失敗; (外鍵現(xiàn)在用的也不多,因?yàn)樗P(guān)聯(lián)性太強(qiáng),如果要?jiǎng)h除一個(gè)表,會(huì)因?yàn)橛型怄I的關(guān)聯(lián)而導(dǎo)致刪除失敗。通常是通過 table a = table b on a.id = b.id 這種兩表關(guān)聯(lián)的方式來(lái)間接替代外鍵作用 )
??3.InnoDB是聚集索引,使用B+Tree作為索引結(jié)構(gòu),數(shù)據(jù)文件是和(主鍵)索引綁在一起的;MyISAM是非聚集索引,它也是使用B+Tree作為索引結(jié)構(gòu),但是索引和數(shù)據(jù)文件是分離的,索引保存的是數(shù)據(jù)文件的指針。
??4.InnoDB 必須要有主鍵,MyISAM可以沒有主鍵
;InnoDB 如果我們沒有明確去指定創(chuàng)建主鍵索引。它會(huì)幫我們隱藏的生成一個(gè) 6 byte 的 int 型的索引作為主鍵索引。
??5. InnoDB支持表級(jí)鎖、行級(jí)鎖,默認(rèn)為行級(jí)鎖;而 MyISAM 僅支持表級(jí)鎖。InnoDB 的行鎖是實(shí)現(xiàn)在索引上的,而不是鎖在物理行上。如果訪問未命中索引,也是無(wú)法使用行鎖,將會(huì)退化為表鎖.
?
17.數(shù)據(jù)庫(kù)隔離級(jí)別?MySQL和Oracle默認(rèn)得隔離級(jí)別?
MySQL支持四種事務(wù)隔離級(jí)別,默認(rèn)的事務(wù)隔離級(jí)別為repeatable read,Oracle數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別是read committed。
四種隔離級(jí)別分別為讀未提交,讀已提交,可重復(fù)讀, 序列化/串行化。其中,讀未提交是最低的隔離級(jí)別,序列化隔離級(jí)別最高。
1.讀未提交(read uncommitted):沒有提交就讀取到了。
2.讀已提交(read committed):已經(jīng)提交的才能讀取。
3.可重復(fù)讀(repeatable read):事務(wù)結(jié)束之前。永遠(yuǎn)讀取不到真實(shí)數(shù)據(jù),提交了也讀取不到,讀取的永遠(yuǎn)都是最初的數(shù)據(jù),即假象。
4.序列化(serializable):表示事務(wù)排隊(duì),不能并發(fā),每次讀取的都是最真實(shí)的數(shù)據(jù)。
同時(shí)運(yùn)行多個(gè)事務(wù),當(dāng)這些事務(wù)訪問數(shù)據(jù)庫(kù)中相同的數(shù)據(jù)時(shí),如果沒有采取必要的隔離機(jī)制,就會(huì)導(dǎo)致各種并發(fā)問題。
1-臟讀:對(duì)于兩個(gè)事務(wù)T1和T2,T1讀取了已經(jīng)被T2更新但還沒有提交的字段,之后,若T2回滾,則T1讀取的數(shù)據(jù)就是臨時(shí)且無(wú)效的。
2-不可重復(fù)讀:對(duì)于兩個(gè)事務(wù)T1和T2,T1讀取了該字段,但是T2更新了該字段,T1再次讀取這個(gè)字段,值就不同了。
3-幻讀:對(duì)于兩個(gè)事務(wù)T1和T2,T1從表中讀取了一些字段,T2在表中插入了一些新的行,T1再次讀取該表發(fā)現(xiàn)多幾行。
read uncommitted:可以出現(xiàn)臟讀,幻讀,不可重復(fù)讀。
read committed:避免臟讀,出現(xiàn)幻讀和不可重復(fù)讀。
repeatable read:避免臟讀和不可重復(fù)讀,出現(xiàn)幻讀。
serializable:避免臟讀,幻讀,不可重復(fù)讀。
?
18.AOP解釋一下?
AOP(Aspect?Oriented Programming):面向切面編程,一種編程范式,隸屬于軟件工程范疇,指導(dǎo)開發(fā)者如何組織程序結(jié)構(gòu),AOP彌補(bǔ)了OOP的不足,基于OOP基礎(chǔ)之上進(jìn)行橫向開發(fā)。其實(shí)重復(fù)的這一部分代碼可以進(jìn)行抽取,簡(jiǎn)化了我們的開發(fā) 。運(yùn)行的時(shí)候需要將其中抽取出來(lái)的共性功能代碼放回去,形成一個(gè)完整的代碼,從而使程序正常運(yùn)行,這樣的一種開發(fā)模式被稱為AOP。
Joinpoint(連接點(diǎn)):我們平常所寫的普通方法在AOP中就是連接點(diǎn)
Pointcut(切入點(diǎn)):挖掉共性功能剩余下來(lái)的方法.
Advice(通知):抽取出來(lái)的共性功能就是通知,最終回以一個(gè)方法的形式呈現(xiàn)
Aspect(切面):共性功能與切入點(diǎn)之間的存在的位置對(duì)應(yīng)關(guān)系
Target(目標(biāo)對(duì)象):挖掉功能的方法對(duì)應(yīng)的類所產(chǎn)生的對(duì)象,這種對(duì)象是無(wú)法直接完成最終工作的.
Weaving(織入):是一個(gè)將挖掉的功能進(jìn)行回填的一個(gè)動(dòng)態(tài)過程。
Proxy(代理):目標(biāo)對(duì)象是無(wú)法直接完成工作的,需要對(duì)其進(jìn)行功能回填,通過創(chuàng)建的代理對(duì)象實(shí)現(xiàn)。
Introduction(引入/引介):就是對(duì)原始對(duì)象無(wú)中生有的添加成員變量或成員方法
?
19.CAS介紹一下?Syschronized?ReentrantLock?Lock?Synchronized鎖升級(jí)?
CAS是實(shí)現(xiàn)樂觀鎖的一種方式,即compare and swap(比較與交換),涉及三個(gè)操作數(shù):
需要讀寫的內(nèi)存值 V,進(jìn)行比較的值 A,擬寫入的新值 B
會(huì)先把要原值A(chǔ)查詢出來(lái),然后只有當(dāng)V=A時(shí)才會(huì)去寫入新值B,如果不相等則說明原值已被別的線程修改過了,就會(huì)去不斷自旋重試再次修改值
CAS存在的問題:ABA問題,CPU開銷大,只能保證一個(gè)共享變量的原子操作
CAS使用場(chǎng)景,讀多寫少,對(duì)于資源競(jìng)爭(zhēng)較少(線程沖突較輕)的情況
- 此時(shí)如果使用synchronized,那么用戶態(tài)、內(nèi)核態(tài)的頻繁切換會(huì)耗費(fèi)很多資源;
- CAS自旋幾率小,性能更高。
Synchronized是悲觀鎖的一種,使用場(chǎng)景,寫沖突多,線程沖突嚴(yán)重,強(qiáng)一致性的場(chǎng)景,此時(shí)CAS自旋概率大,會(huì)浪費(fèi)更多CPU資源。synchronized是JVM的內(nèi)置鎖,內(nèi)部通過監(jiān)視器鎖實(shí)現(xiàn),基于monitorenter和monitorexit實(shí)現(xiàn)代碼塊同步,每個(gè)同步對(duì)象都有一個(gè)自己的監(jiān)視器鎖,需要判斷對(duì)象能否拿到監(jiān)視器鎖,如果拿到監(jiān)視器鎖,才能進(jìn)入同步塊執(zhí)行同步邏輯,否則需要進(jìn)入同步隊(duì)列等待。
ReentrantLock使用場(chǎng)景:
synchronized的鎖升級(jí)是不可逆的。如果是一個(gè)打車軟件,那過了打車高峰期,還是重量級(jí)鎖,就會(huì)降低效率;此時(shí)如果用Reentratlock就比較好。
可重入鎖的概念:自己可以再次獲取自己的內(nèi)部鎖。比如一個(gè)線程獲得了某個(gè)對(duì)象的鎖,此時(shí)這個(gè)對(duì)象鎖還沒有釋放,當(dāng)其再次想要獲取這個(gè)對(duì)象的鎖的時(shí)候還是可以獲取的,如果不可鎖重入的話,就會(huì)造成死鎖。同一個(gè)線程每次獲取鎖,鎖的計(jì)數(shù)器都是自增1,所以要等到鎖的計(jì)數(shù)器下降為0時(shí)才能釋放鎖。
ReentrantLock使用起來(lái)比較靈活,但是必須有釋放鎖的配合動(dòng)作
ReentrantLock必須手動(dòng)獲取與釋放鎖,而synchronized不需要手動(dòng)釋放和開啟鎖
ReentrantLock只適用于代碼塊鎖,而synchronized可以修飾類、方法、變量等
兩者的鎖機(jī)制其實(shí)也是不一樣的。ReentrantLock底層調(diào)用的是Unsafe的park方法加鎖synchronized操作的應(yīng)該是對(duì)象頭中mark word
?
鎖升級(jí)過程:由無(wú)鎖->偏向鎖->輕量級(jí)鎖->重量級(jí)鎖。偏向鎖:只有一個(gè)線程進(jìn)入臨界區(qū)訪問同步塊。輕量級(jí)鎖:多線程競(jìng)爭(zhēng)不激烈,同步塊執(zhí)行響應(yīng)快。重量級(jí)鎖:多線程競(jìng)爭(zhēng),同步塊執(zhí)行時(shí)間較長(zhǎng)。
1 無(wú)鎖
?當(dāng)一個(gè)對(duì)象被創(chuàng)建之后,還沒有線程進(jìn)入,這個(gè)時(shí)候?qū)ο筇幱跓o(wú)鎖狀態(tài),其Mark Word中的信息如上表所示。
2 偏向鎖
?當(dāng)鎖處于無(wú)鎖狀態(tài)時(shí),有一個(gè)線程A訪問同步塊并獲取鎖時(shí),會(huì)在對(duì)象頭和棧幀中的鎖記錄記錄線程ID,以后該線程在進(jìn)入和退出同步塊時(shí)不需要進(jìn)行CAS操作來(lái)進(jìn)行加鎖和解鎖,只需要簡(jiǎn)單的測(cè)試一下啊對(duì)象頭中的線程ID和當(dāng)前線程是否一致。
3 輕量級(jí)鎖
?在偏向鎖的基礎(chǔ)上,又有另外一個(gè)線程B進(jìn)來(lái),這時(shí)判斷對(duì)象頭中存儲(chǔ)的線程A的ID和線程B不一致,就會(huì)使用CAS競(jìng)爭(zhēng)鎖,并且升級(jí)為輕量級(jí)鎖,會(huì)在線程棧中創(chuàng)建一個(gè)鎖記錄(lock Record),將Mark Word復(fù)制到鎖記錄中,然后線程嘗試使用CAS將對(duì)象頭的Mark Word替換成指向鎖記錄的指針,如果成功,則當(dāng)前線程獲得鎖;失敗,表示其他線程競(jìng)爭(zhēng)鎖,當(dāng)前線程便嘗試CAS來(lái)獲取鎖。
4 重量級(jí)鎖
?當(dāng)線程沒有獲得輕量級(jí)鎖時(shí),線程會(huì)CAS自旋來(lái)獲取鎖,當(dāng)一個(gè)線程自旋10次之后,仍然未獲得鎖,那么就會(huì)升級(jí)成為重量級(jí)鎖。? 成為重量級(jí)鎖之后,線程會(huì)進(jìn)入阻塞隊(duì)列(EntryList),線程不再自旋獲取鎖,而是由CPU進(jìn)行調(diào)度,線程串行執(zhí)行。
?
20.算法題:二叉樹的先序,中序,后序遍歷
先將先序遍歷結(jié)果存到集合里面,先序遍歷遞歸注意遞歸出口,最后從集合李取元素存到數(shù)組。
先序遍歷題目:二叉樹的前序遍歷_牛客題霸_??途W(wǎng)
import java.util.*;/** public class TreeNode {* int val = 0;* TreeNode left = null;* TreeNode right = null;* public TreeNode(int val) {* this.val = val;* }* }*/public class Solution {/*** 代碼中的類名、方法名、參數(shù)名已經(jīng)指定,請(qǐng)勿修改,直接返回方法規(guī)定的值即可** * @param root TreeNode類 * @return int整型一維數(shù)組*/public static void preOrder(TreeNode root, ArrayList<Integer> arrayList){if(root == null){ //遞歸出口return ;}arrayList.add(root.val) ;preOrder(root.left,arrayList) ;preOrder(root.right,arrayList) ;}public int[] preorderTraversal (TreeNode root) {// write code hereArrayList<Integer> arrayList = new ArrayList<> () ;preOrder(root, arrayList) ;int [] ans = new int [arrayList.size()] ;for(int i=0; i<ans.length; i++){ans[i] = arrayList.get(i) ;}return ans ;}}
中序遍歷題目:二叉樹的中序遍歷_??皖}霸_牛客網(wǎng)
import java.util.*;/** public class TreeNode {* int val = 0;* TreeNode left = null;* TreeNode right = null;* public TreeNode(int val) {* this.val = val;* }* }*/public class Solution {/*** 代碼中的類名、方法名、參數(shù)名已經(jīng)指定,請(qǐng)勿修改,直接返回方法規(guī)定的值即可** * @param root TreeNode類 * @return int整型一維數(shù)組*/public int[] inorderTraversal (TreeNode root) {// write code hereArrayList<Integer> arraylist = new ArrayList<>() ;inOrder(root, arraylist) ;int [] ans = new int [arraylist.size()] ;for(int i=0; i<ans.length; i++){ans[i] = arraylist.get(i) ;}return ans ;}public static void inOrder(TreeNode root, ArrayList<Integer> arraylist){if(root == null){return ;}inOrder(root.left,arraylist) ;arraylist.add(root.val) ;inOrder(root.right,arraylist) ;}
}
后序遍歷題目:二叉樹的后序遍歷_??皖}霸_??途W(wǎng)
import java.util.*;/** public class TreeNode {* int val = 0;* TreeNode left = null;* TreeNode right = null;* public TreeNode(int val) {* this.val = val;* }* }*/public class Solution {/*** 代碼中的類名、方法名、參數(shù)名已經(jīng)指定,請(qǐng)勿修改,直接返回方法規(guī)定的值即可** * @param root TreeNode類 * @return int整型一維數(shù)組*/public int[] postorderTraversal (TreeNode root) {// write code hereArrayList<Integer> arraylist = new ArrayList<>() ;postOrder(root, arraylist) ;int [] ans = new int [arraylist.size()] ;for(int i=0; i<ans.length; i++){ans[i] = arraylist.get(i) ;}return ans ;}public void postOrder(TreeNode root, ArrayList<Integer> arraylist){if(root == null){return ;}postOrder(root.left,arraylist) ;postOrder(root.right,arraylist) ;arraylist.add(root.val) ;}
}