**三十一、Redis分布式

Redis分布式鎖注意事項:
1.設(shè)置過期時間:如果獲取鎖后,系統(tǒng)宕機了,沒有釋放鎖,導(dǎo)致其他線程一致拿不到該鎖,影響業(yè)務(wù)功能,加鎖和設(shè)置過期時間是原子操作
2.過期時間到了業(yè)務(wù)操作還沒完成:增加守護線程,定時對鎖的過期時間進行續(xù)期
3.鎖的釋放:加鎖和釋放必須是同一個人操作,釋放鎖先判斷是不是自己加的
Redission實現(xiàn)分布式鎖,是redis的客戶端,基于Lua腳本執(zhí)行,key的模式過期時間30秒,加鎖之后會基于watchDog(看門狗)機制在后臺啟動一個線程每隔10秒執(zhí)行一次檢查,如果key還在,重置key的生存時間為30秒,redisson也實現(xiàn)可重入鎖的機制,再次加鎖會將key對應(yīng)的hash結(jié)構(gòu)中value+1.
Redission的好處:
自動續(xù)期功能:watchDog(看門狗)機制在后臺啟動一個線程每隔10秒執(zhí)行一次檢查,如果key還在,重置key的生存時間為30秒
基本不用寫代碼只能可以拿來用
過期時間:默認(rèn)30秒

**三十二、Redis與Memcache區(qū)別:
-
數(shù)據(jù)類型:memcached只支持簡單數(shù)據(jù)類型,鍵值對
Redis:字符串、集合、列表等 內(nèi)存管理:redis可以設(shè)置過期時間
持久化管理:memcached不提供持久化功能,數(shù)據(jù)僅保存在內(nèi)存中;redis支持持久化,將數(shù)據(jù)保存到磁盤上,防止數(shù)據(jù)丟失。Reids持久化:RDF、AOF、混合方式
使用場景:
緩存內(nèi)容只有字符串、不考慮數(shù)據(jù)持久性、數(shù)據(jù)過期,可以采用memcached
**三十三、HashMap 的實現(xiàn)原理?
HashMap實際是一種“數(shù)組+鏈表”數(shù)據(jù)結(jié)構(gòu),jdk8后數(shù)組+鏈表+紅黑樹
HashMap是基于哈希表的Map接口的非同步實現(xiàn)。實現(xiàn)HashMap對數(shù)據(jù)的操作,允許有一個null鍵,多個null值。
HashMap底層就是一個數(shù)組結(jié)構(gòu),數(shù)組中的每一項又是一個鏈表。數(shù)組+鏈表結(jié)構(gòu),新建一個HashMap的時候,就會初始化一個數(shù)組。Entry就是數(shù)組中的元素,每個Entry其實就是一個key-value的鍵值對,它持有一個指向下一個元素的引用,這就構(gòu)成了鏈表,HashMap底層將key-value當(dāng)成一個整體來處理,這個整體就是一個Entry對象。HashMap底層采用一個Entry【】數(shù)組來保存所有的key-value鍵值對,當(dāng)需要存儲一個Entry對象時,會根據(jù)hash算法來決定在其數(shù)組中的位置,在根據(jù)equals方法決定其在該數(shù)組位置上的鏈表中的存儲位置;當(dāng)需要取出一個Entry對象時,也會根據(jù)hash算法找到其在數(shù)組中的存儲位置, 在根據(jù)equals方法從該位置上的鏈表中取出Entry;

**三十四、HashMap,HashTable,ConcurrentHash的共同點和區(qū)別
- HashMap
底層由鏈表+數(shù)組+紅黑樹實現(xiàn)
可以存儲null鍵和null值
線性不安全
初始容量為16,擴容每次都是2的n次冪
加載因子為0.75,當(dāng)Map中元素總數(shù)超過Entry數(shù)組的0.75,觸發(fā)擴容操作.
并發(fā)情況下,HashMap進行put操作會引起死循環(huán),導(dǎo)致CPU利用率接近100%
HashMap是對Map接口的實現(xiàn)
2.HashTable
HashTable的底層也是由鏈表+數(shù)組+紅黑樹實現(xiàn)。
不能存儲null鍵或null值
它是線性安全的,使用了synchronized關(guān)鍵字。
HashTable實現(xiàn)了Map接口和Dictionary抽象類
Hashtable初始容量為11
3.ConcurrentHashMap
ConcurrentHashMap的底層是數(shù)組+鏈表+紅黑樹
不能存儲null鍵或null值
ConcurrentHashMap是線程安全的
ConcurrentHashMap使用鎖分段技術(shù)確保線性安全,JDK8為何又放棄分段鎖,是因為多個分段鎖浪費內(nèi)存空間,競爭同一個鎖的概率非常小,分段鎖反而會造成效率低。
**三十五、面向?qū)ο?/h3>
面向?qū)ο蟮娜齻€基本特征:封裝、繼承、多態(tài)
1)、封裝(英語:Encapsulation)是指,一種將抽象性函式接口的實現(xiàn)細(xì)節(jié)部份包裝、隱藏起來的方法。封裝可以被認(rèn)為是一個保護屏障,防止該類的代碼和數(shù)據(jù)被外部類定義的代碼隨機訪問。
2)、繼承可以使得子類具有父類的屬性和方法或者重新定義、追加屬性和方法等,繼承可以理解為一個對象從另一個對象獲取屬性的過程。
3)、多態(tài)是同一個行為具有多個不同表現(xiàn)形式或形態(tài)的能力。多態(tài)性是對象多種表現(xiàn)形式的體現(xiàn)。比如我們說"寵物"這個對象,它就有很多不同的表達(dá)或?qū)崿F(xiàn),比如有小貓、小狗、蜥蜴等等。那么我到寵物店說"請給我一只寵物",服務(wù)員給我小貓、小狗或者蜥蜴都可以,我們就說"寵物"這個對象就具備多態(tài)性。
Java 中實現(xiàn)多態(tài)的機制是什么?
方法的重寫 Overriding 和重載 Overloading 是 Java 多態(tài)性的不同表現(xiàn)。重寫 Overriding是父類與子類之間多態(tài)性的一種表現(xiàn),重載 Overloading 是一個類中多態(tài)性的一種表現(xiàn)
**三十六、抽象類(abstract class)和接口(interface)有什么區(qū)別?
1)、抽象類和接口都不能夠?qū)嵗?/p>
2)、接口用interface來修飾
3)、類能實現(xiàn)一個或多個接口,但只能繼承一個抽象類
4)、接口里的方法只能是抽象方法、類方法或者默認(rèn)方法,接口里的方法不能有方法實現(xiàn),但類方法、默認(rèn)方法都必須要實現(xiàn)。
**三十七、為什么重載hashCode方法?
一般的地方不需要重載hashCode,只有當(dāng)類需要放在HashTable、HashMap、HashSet等等hash結(jié)構(gòu)的集合時才會重載hashCode,那么為什么要重載hashCode呢?
如果你重寫了equals,比如說是基于對象的內(nèi)容實現(xiàn)的,而保留hashCode的實現(xiàn)不變,那么很可能某兩個對象明明是“相等”,而hashCode卻不一樣。
這樣,當(dāng)你用其中的一個作為鍵保存到hashMap、hasoTable或hashSet中,再以“相等的”找另一個作為鍵值去查找他們的時候,則根本找不到。
**三十八、== 和 equals 的區(qū)別是什么?
==是操作符,是比較兩個對象的地址或基本類型,equals是比較兩個對象的內(nèi)容,屬于Object里的方法。
**三十九、SQL優(yōu)化
索引查詢、避免全表掃描:查詢數(shù)據(jù)庫的數(shù)據(jù)盡量使用索引來查詢,避免全表掃描。盡量只查詢索引條件的字段
查詢數(shù)據(jù)盡量避免使用or:使用or會導(dǎo)致執(zhí)行sql的時候進行數(shù)據(jù)范圍的索引掃描或者全表掃描,效率降低。
Where條件中:in和not in、對字段進行表達(dá)式操作、對字段進行函數(shù)操作都會導(dǎo)致全表搜索
多張表數(shù)據(jù)查詢,使用inner join代替自查詢,因為子查詢需要在內(nèi)存中創(chuàng)建臨時表來完成這個邏輯上的需要兩個步驟的查詢工作。
使用like進行數(shù)據(jù)表查詢時,能用%就不建議使用雙%:雙%查詢會導(dǎo)致mysql引擎放棄使用索引而進行全表掃描查詢,查詢時盡量把%放后面,或者不適用%。
慢查詢?nèi)罩荆郝樵內(nèi)罩镜氖褂?,在調(diào)試的時候開啟慢查詢,定位的慢查詢語句,再做優(yōu)化策略。關(guān)閉/開啟語句 Slow_query_log=0|1,Long_query_time=N超過該時間臨界點,就為慢查詢
mysql查看sql的執(zhí)行計劃,以此來分析sql執(zhí)行緩慢的問題所在
**四十、鎖
公平鎖:指多個線程按照申請鎖的順序來獲取鎖。
非公平鎖:指多個線程獲取鎖的順序并不是按照申請鎖的順序,有可能后申請的線程比先申請的線程優(yōu)先獲取鎖。有可能,會造成優(yōu)先級反轉(zhuǎn)或者饑餓現(xiàn)象。
可重入鎖:指的是可重復(fù)可遞歸調(diào)用的鎖,在外層使用鎖之后,在內(nèi)層仍然可以使用,并且不發(fā)生死鎖(前提得是同一個對象或者class),例如ReentrantLock和synchronized都是可重入鎖。防止死鎖
不可重入鎖:不可遞歸調(diào)用,遞歸調(diào)用就發(fā)生死鎖
獨享鎖:是指該鎖一次只能被一個線程所持有。 共享鎖:是指該鎖可被多個線程所持有。
悲觀鎖:認(rèn)為對于同一個數(shù)據(jù)的并發(fā)操作,一定是會發(fā)生修改的,哪怕沒有修改,也會認(rèn)為修改。因此對于同一個數(shù)據(jù)的并發(fā)操作,悲觀鎖采取加鎖的形式。悲觀的認(rèn)為,不加鎖的并發(fā)操作一定會出問題
樂觀鎖:則認(rèn)為對于同一個數(shù)據(jù)的并發(fā)操作,是不會發(fā)生修改的。在更新數(shù)據(jù)的時候,會采用嘗試更新,不斷重新的方式更新數(shù)據(jù)。樂觀的認(rèn)為,不加鎖的并發(fā)操作是沒有事情的。
Java鎖機制可歸為Sychornized鎖和Lock鎖兩類。
Synchronized是基于JVM來保證數(shù)據(jù)同步的,底層使用指令碼方式來控制鎖的,映射成字節(jié)碼指令就是增加來兩個指令:monitorenter和monitorexit。當(dāng)線程執(zhí)行遇到monitorenter指令時會嘗試獲取內(nèi)置鎖,如果獲取鎖則鎖計數(shù)器+1,如果沒有獲取鎖則阻塞;當(dāng)遇到monitorexit指令時鎖計數(shù)器-1,如果計數(shù)器為0則釋放鎖。
Lock底層是CAS樂觀鎖,是在硬件層面,通過特殊的CPU指令實現(xiàn)數(shù)據(jù)同步的,依賴AbstractQueuedSynchronizer類,把所有的請求線程構(gòu)成一個CLH隊列。而對該隊列的操作均通過Lock-Free(CAS)操作。與synchronized不同的是,Lock鎖是純Java實現(xiàn)的,與底層的JVM無關(guān)。在java.util.concurrent.locks包中有很多Lock的實現(xiàn)類,常用的有ReentrantLock、ReentrantReadWriteLock,java的Lock接口的子類就是借助AQS來實現(xiàn)了lock和unlock。
ReentrantLock原理
ReentrantLock主要利用CAS+AQS隊列來實現(xiàn),支持公平鎖和非公平鎖。ReentrantLock的基本實現(xiàn)可以概括為:先通過CAS嘗試獲取鎖。如果此時已經(jīng)有線程占據(jù)了鎖,那就加入AQS隊列并且被掛起。當(dāng)鎖被釋放之后,排在CLH隊列隊首的線程會被喚醒,然后CAS再次嘗試獲取鎖。此時,如果:
非公平鎖:如果同時還有另一個線程進來嘗試獲取,那么有可能會讓這個線程搶先獲取
公平鎖:如果同時還有另一個線程進來嘗試獲取,當(dāng)它發(fā)現(xiàn)自己不是在隊首的話,就會排到隊尾,由隊首的線程獲取到鎖
ReentrantLock默認(rèn)構(gòu)造器初始化為NonfairSync對象,由lock()和unlock的源碼可以看到,它們分別調(diào)用了sync對象的lock()和release(1)方法。
Lock與synchronized 的區(qū)別
synchronized會自動釋放鎖、ReentrantLock需要手動釋放鎖(在finally塊中顯示釋放鎖)
Synchronized是非公平鎖,ReentrantLock是可以是公平也可以是非公平的(默認(rèn)非公平)
synchronized是在JVM層面上實現(xiàn)的,Lock是用CAS來實現(xiàn)的