一.ArrayList和LinkedList區(qū)別及使用場景
LinkedList和ArrayList的差別主要來自于Array和LinkedList數(shù)據(jù)結(jié)構(gòu)的不同。ArrayList是基于數(shù)組實現(xiàn)的醋安,LinkedList是基于雙鏈表實現(xiàn)的挚瘟。另外LinkedList類不僅是List接口的實現(xiàn)類,可以根據(jù)索引來隨機訪問集合中的元素摩梧,除此之外,LinkedList還實現(xiàn)了Deque接口,Deque接口是Queue接口的子接口麸祷,它代表一個雙向隊列,因此LinkedList可以作為雙向隊列 褒搔,棧(可以參見Deque提供的接口方法)和List集合使用阶牍,功能強大。
因為Array是基于索引(index)的數(shù)據(jù)結(jié)構(gòu)星瘾,它使用索引在數(shù)組中搜索和讀取數(shù)據(jù)是很快的走孽,可以直接返回數(shù)組中index位置的元素,因此在隨機訪問集合元素上有較好的性能琳状。Array獲取數(shù)據(jù)的時間復(fù)雜度是O(1),但是要插入磕瓷、刪除數(shù)據(jù)卻是開銷很大的,因為這需要移動數(shù)組中插入位置之后的的所有元素念逞。
相對于ArrayList生宛,LinkedList的隨機訪問集合元素時性能較差,因為需要在雙向列表中找到要index的位置肮柜,再返回陷舅;但在插入,刪除操作是更快的审洞。因為LinkedList不像ArrayList一樣莱睁,不需要改變數(shù)組的大小,也不需要在數(shù)組裝滿的時候要將所有的數(shù)據(jù)重新裝入一個新的數(shù)組芒澜,這是ArrayList最壞的一種情況仰剿,時間復(fù)雜度是O(n),而LinkedList中插入或刪除的時間復(fù)雜度僅為O(1)痴晦。ArrayList在插入數(shù)據(jù)時還需要更新索引(除了插入數(shù)組的尾部)南吮。
LinkedList需要更多的內(nèi)存,因為ArrayList的每個索引的位置是實際的數(shù)據(jù)誊酌,而LinkedList中的每個節(jié)點中存儲的是實際的數(shù)據(jù)和前后節(jié)點的位置部凑。
使用場景:
(1)如果應(yīng)用程序?qū)?shù)據(jù)有較多的隨機訪問露乏,ArrayList對象要優(yōu)于LinkedList對象;
(2)如果應(yīng)用程序有更多的插入或者刪除操作涂邀,較少的數(shù)據(jù)讀取瘟仿,LinkedList對象要優(yōu)于ArrayList對象;
(3)不過ArrayList的插入比勉,刪除操作也不一定比LinkedList慢劳较,如果在List靠近末尾的地方插入,那么ArrayList只需要移動較少的數(shù)據(jù)浩聋,而LinkedList則需要一直查找到列表尾部观蜗,反而耗費較多時間,這時ArrayList就比LinkedList要快衣洁。
二.hashmap如何解決hash沖突墓捻,為什么hashmap中的鏈表需要轉(zhuǎn)成紅黑樹?
1.hash值沖突是發(fā)生在put()時闸与,先查詢是否存在該hash值毙替,若不存在,則直接以Entry<V,V>的方式存放在數(shù)組中践樱,若存在厂画,則再對比key是否相同,若hash值和key都相同,則替換value拷邢,若hash值相同袱院,key不相同,則形成一個單鏈表瞭稼,將hash值相同忽洛,key不同的元素以Entry<V,V>的方式存放在鏈表中,這樣就解決了hash沖突
2.在jdk1.8版本后环肘,Java對HashMap做了改進欲虚,在鏈表長度大于8的時候,將后面的數(shù)據(jù)存在紅黑樹中悔雹,以加快檢索速度复哆。
三.JDK1.7 并發(fā)的HashMap為什么會引起死循環(huán)?
線程不安全的HashMap, HashMap在并發(fā)執(zhí)行put操作時會引起死循環(huán),是因為多線程會導(dǎo)致HashMap的Entry鏈表形成環(huán)形數(shù)據(jù)結(jié)構(gòu)腌零,查找時會陷入死循環(huán)梯找。
table這個Entry數(shù)組是線程共享的,而next,e是線程私有的益涧。一個map中存有3->7->null兩個元素锈锤,當兩個線程put第三個元素8時,如果出發(fā)了resize(),A線程走到transfer()方法時B線程進來了久免,
B線程會把老的table在自己線程中變成一個新的table寫入內(nèi)存浅辙,修改了node的指向關(guān)系,變成了7->3->null妄壶;這時A線程再拿到B修改過的table去變成新的table摔握,在循環(huán)讀取元素時寄狼,先拿到3放入新的table丁寄,
線程A再復(fù)制元素 7 ,當前 e = 7 ,而next值由于線程 B 修改了它的引用泊愧,所以next 為 3伊磺,由于上面取到的next = 3, 接著while循環(huán),即當前處理的結(jié)點為3删咱, next就為null 屑埋,退出while循環(huán)。
這時node的指向關(guān)系是痰滋,7->3->7形成循環(huán)摘能,在get操作時會死循環(huán)
四.hashmap的數(shù)組長度為什么要保證是2的冪
關(guān)鍵代碼:tab[i = (n - 1) & hash] 這里是計算數(shù)組索引下標的位置
&為二進制中的與運算
運算規(guī)則:0&0=0; 0&1=0; 1&0=0; 1&1=1;
即:兩位同時為“1”,結(jié)果才為“1”敲街,否則為0
因為hashMap 的數(shù)組長度都是2的n次冪 团搞,那么對于這個數(shù)再減去1,轉(zhuǎn)換成二進制的話多艇,就肯定是最高位為0逻恐,其他位全是1 的數(shù)。
不為2的n次冪 的話峻黍,對應(yīng)的二進制數(shù)肯定有一位為0 , 這樣不管你的hashCode 值對應(yīng)的該位复隆,是0還是1 ,
最終得到的該位上的數(shù)肯定是0,這帶來的問題就是HashMap上的數(shù)組元素分布不均勻姆涩,而數(shù)組上的某些位置挽拂,永遠也用不到。
五.如何用LinkedHashMap實現(xiàn)LRU
LinkedHashMap默認的元素順序是put的順序骨饿,但是如果使用帶參數(shù)的構(gòu)造函數(shù)亏栈,那么LinkedHashMap會根據(jù)訪問順序來調(diào)整內(nèi)部 順序。 LinkedHashMap的get()方法除了返回元素之外還可以把被訪問的元素放到鏈表的底端样刷,這樣一來每次頂端的元素就是remove的元素仑扑。
public LinkedHashMap (int initialCapacity, float loadFactor, boolean accessOrder);
initialCapacity 初始容量
loadFactor 加載因子置鼻,一般是 0.75f
accessOrder false 基于插入順序 true 基于訪問順序(get一個元素后镇饮,這個元素被加到最后,使用了LRU 最近最少被使用的調(diào)度算法)
LinkedHashMap中有一個方法removeEldestEntry(entry) 我們只需要覆蓋這個方法,根據(jù)我們自己的需求在一定條件下返回true,這樣就實現(xiàn)了LruCache
六.ConcurrentHashMap是如何在保證并發(fā)安全的同時提高性能
HashTable容器在競爭激烈的并發(fā)環(huán)境下表現(xiàn)出效率低下的原因箕母,是因為所有訪問HashTable的線程都必須競爭同一把鎖储藐,那假如容器里有多把鎖俱济,每一把鎖用于鎖容器其中一部分數(shù)據(jù),
那么當多線程訪問容器里不同數(shù)據(jù)段的數(shù)據(jù)時钙勃,線程間就不會存在鎖競爭蛛碌,從而可以有效的提高并發(fā)訪問效率,這就是ConcurrentHashMap所使用的鎖分段技術(shù)辖源,首先將數(shù)據(jù)分成一段一段的存儲蔚携,
然后給每一段數(shù)據(jù)配一把鎖,當一個線程占用鎖訪問其中一個段數(shù)據(jù)的時候克饶,其他段的數(shù)據(jù)也能被其他線程訪問
七.ConcurrentHashMap是如何讓多線程同時參與擴容
先判斷table的長度是否小于64酝蜒,如果小于,則優(yōu)先使用擴容來解決問題矾湃,擴容為原來的一倍亡脑,調(diào)整某一個桶中元素過多的問題(超出了8個)),會觸發(fā)某些桶中的元素重新分配邀跃,避免在一個桶中有太多的元素影響訪問效率
在多線的環(huán)境下霉咨,用volatile的方式讀取sizectrl屬性的值,來判斷map所處的狀態(tài)拍屑,通過cas修改操作來告訴其它線程Map的狀態(tài)類型途戒。
未初始化
等于0,表示未指定初始化容量丽涩,則使用默認容量
大于0棺滞,為指定的初始化容量
初始化中
等于-1,表示正在初始化矢渊,并且通過cas告訴其它線程
正常狀態(tài)
等于原table長度n*0.75继准,擴容閥值
擴容中
小于0,表示有其他線程正在執(zhí)行擴容操作
等于(resizeStamp(n) << RESIZE_STAMP_SHIFT) + 2表示此時只有一個線程在執(zhí)行擴容
并且多線程分段擴容
八.ReentrantLock如何實現(xiàn)公平和非公平鎖是如何實現(xiàn)
公平鎖的lock方法在進行cas判斷時多了一個hasQueuedPredecessors()方法矮男,當阻塞隊列中沒有線程時才嘗試獲取鎖
九.CountDownLatch和CyclicBarrier的區(qū)別移必?各自適用于什么場景?
CountDownLatch和CyclicBarrier都是java.util.concurrent包下面的多線程工具類毡鉴。
區(qū)別:CountDownLatch崔泵,當計數(shù)為0的時候,下一步的動作實施者是main函數(shù)猪瞬;對于CyclicBarrier憎瘸,下一步動作實施者是“其他線程”
對于CountDownLatch,其他線程為游戲玩家陈瘦,比如英雄聯(lián)盟幌甘,主線程為控制游戲開始的線程。在所有的玩家都準備好之前,主線程是處于等待狀態(tài)的锅风,也就是游戲不能開始酥诽。當所有的玩家準備好之后,下一步的動作實施者為主線程皱埠,即開始游戲肮帐。
公司要求所有人在翻越當前障礙物之后再開始翻越下一個障礙物,也就是所有人翻越第一個障礙物之后边器,才開始翻越第二個训枢,以此類推。類比地饰抒,每一個員工都是一個“其他線程”肮砾。當所有人都翻越的所有的障礙物之后诀黍,程序才結(jié)束袋坑。而主線程可能早就結(jié)束了,這里我們不用管主線程
十.往線程池里提交一個任務(wù)會發(fā)生什么
當調(diào)用execute()方法添加一個任務(wù)時眯勾,線程池會做如下判斷:
a. 如果正在運行的線程數(shù)量小于corePoolSize枣宫,那么馬上創(chuàng)建線程運行這個任務(wù)
b. 如果正在運行的線程數(shù)量大于或等于corePoolSize,那么將這個任務(wù)放入隊列
c. 如果這時候隊列滿了吃环,而且正在運行的線程數(shù)量小于maximunPoolSize也颤,那么還是要創(chuàng)建非核心線程立刻運行這個任務(wù)
d. 如果隊列滿了,而且正在運行的線程數(shù)量大于或等于maximunPoolSize郁轻,那么線程池會拋出RejectedExecutionException
十一.線程池的幾個參數(shù)如何設(shè)置
corePoolSize 核心線程池大小
maximumPoolSize 線程池最大容量大小
keepAliveTime 線程池空閑時翅娶,線程存活的時間
TimeUnit 線程活動保持時間的單位
BlockingQueue<Runnable> 任務(wù)隊列,用于保存等待執(zhí)行的任務(wù)的阻塞隊列
ThreadFactory 用于設(shè)置線程的工廠
RejectedExecutionHandler 飽和策略
十二.線程池的非核心線程什么時候會被釋放
阻塞隊列里沒有任務(wù)好唯,那么非核心線程也要在等到keepAliveTime時間后才會釋放
十三.synchronized與ReentrantLock的區(qū)別
sychronized有三條原則:
當一個線程訪問“某對象”的“synchronized方法”或者“synchronized代碼塊”時竭沫,其他線程對“該對象”的該“synchronized方法”或者“synchronized代碼塊”的訪問將被阻塞。
當一個線程訪問“某對象”的“synchronized方法”或者“synchronized代碼塊”時骑篙,其他線程仍然可以訪問“該對象”的非同步代碼塊蜕提。
當一個線程訪問“某對象”的“synchronized方法”或者“synchronized代碼塊”時,其他線程對“該對象”的其他的“synchronized方法”或者“synchronized代碼塊”的訪問將被阻塞靶端。
synchronized會在進入同步塊的前后分別形成monitorenter和monitorexit字節(jié)碼指令.在執(zhí)行monitorenter指令時會嘗試獲取對象的鎖,如果此沒對象沒有被鎖,或者此對象已經(jīng)被當前線程鎖住,
那么鎖的計數(shù)器加一,每當monitorexit被鎖的對象的計數(shù)器減一.直到為0就釋放該對象的鎖.由此synchronized是可重入的,不會出現(xiàn)自己把自己鎖死.
ReentrantLock:
除了synchronized的功能谎势,多了三個高級功能
等待可中斷:在持有鎖的線程長時間不釋放鎖的時候,等待的線程可以選擇放棄等待杨名,tryLock(long timeout, TimeUnit unit)
公平鎖:按照申請鎖的順序來一次獲得鎖稱為公平鎖脏榆,synchronized的是非公平鎖,ReentrantLock可以通過構(gòu)造函數(shù)實現(xiàn)公平鎖台谍。new RenentrantLock(boolean fair)
綁定多個Condition:通過多次newCondition可以獲得多個Condition對象须喂,可以簡單的實現(xiàn)比較負責的線程同步的功能,通過await()等待,signal()喚醒;
十四.樂觀鎖和悲觀鎖的區(qū)別
樂觀鎖:相信事務(wù)之間的數(shù)據(jù)競爭(data race)的概率是比較小的,因此盡可能直接做下去镊折,直到提交的時候才去鎖定胯府,所以不會產(chǎn)生任何鎖和死鎖。樂觀鎖不是數(shù)據(jù)庫自帶的恨胚,需要我們自己去實現(xiàn)骂因。
悲觀鎖:就是在操作數(shù)據(jù)時,認為此操作會出現(xiàn)數(shù)據(jù)沖突赃泡,所以在進行每次操作時都要通過獲取鎖才能進行對相同數(shù)據(jù)的操作寒波。悲觀鎖是由數(shù)據(jù)庫自己實現(xiàn)了的。
使用select…for update會把數(shù)據(jù)給鎖住升熊,不過我們需要注意一些鎖的級別俄烁,MySQL InnoDB默認行級鎖。行級鎖都是基于索引的级野,如果一條SQL語句用不到索引是不會使用行級鎖的页屠,會使用表級鎖把整張表鎖住,這點需要注意蓖柔。
十五.如何實現(xiàn)一個樂觀鎖
比如多線程實現(xiàn)a++操作辰企。如果簡單循環(huán)創(chuàng)建線程執(zhí)行a++,最終結(jié)果不一定是預(yù)期的樣子况鸣,因為a++操作需要三步完成:1.從主內(nèi)存中讀取a的值到線程內(nèi)存2.對a進行加1操作3.把值寫入主內(nèi)存牢贸。所以可能一個線程
還沒寫回主內(nèi)存,其他線程就拿到舊的值進行加1镐捧∏彼鳎可以參考AtomicInteger的實現(xiàn)就利用了樂觀鎖。原理是自增操作主要是使用了unsafe的getAndAddInt方法懂酱,其內(nèi)部又調(diào)用了Unsafe.compareAndSwapInt方法竹习。這個機制叫做CAS機制。
CAS 即比較并替換玩焰,實現(xiàn)并發(fā)算法時常用到的一種技術(shù)由驹。CAS操作包含三個操作數(shù)——內(nèi)存位置、預(yù)期原值及新值昔园。執(zhí)行CAS操作的時候蔓榄,將內(nèi)存位置的值與預(yù)期原值比較,如果相匹配默刚,那么處理器會自動將該位置值更新為新值甥郑,否則,處理器不做任何操作荤西。
十六.Java中的強引用澜搅、軟引用伍俘、弱引用寻行、虛引用
強引用:如果一個對象具有強引用带膜,不會被垃圾回收器回收。當內(nèi)存空間不足翰铡,Java虛擬機寧愿拋出OutOfMemoryError錯誤饵溅,使程序異常終止妨退,也不回收這種對象。
軟引用:軟引用關(guān)聯(lián)著的對象蜕企,只有在內(nèi)存不足的時候JVM才會回收該對象
Obj obj = new Obj();
SoftReference<Obj> sr = new SoftReference<Obj>(obj);
obj = null;
System.out.println(sr.get());
弱引用:當JVM進行垃圾回收時咬荷,無論內(nèi)存是否充足,都會回收被弱引用關(guān)聯(lián)的對象
WeakReference<String> sr = new WeakReference<String>(new String("hello"));
System.out.println(sr.get()); // 輸出hello
System.gc(); //通知JVM的gc進行垃圾回收
System.out.println(sr.get()); // 輸出null
虛引用:不影響對象的生命周期轻掩。在java中用java.lang.ref.PhantomReference類表示幸乒。如果一個對象與虛引用關(guān)聯(lián),則跟沒有引用與之關(guān)聯(lián)一樣唇牧,在任何時候都可能被垃圾回收器回收罕扎。虛引用主要用來跟蹤對象被垃圾回收的活動。
虛引用必須和引用隊列關(guān)聯(lián)使用奋构,當垃圾回收器準備回收一個對象時壳影,如果發(fā)現(xiàn)它還有虛引用,就會把這個虛引用加入到與之 關(guān)聯(lián)的引用隊列中弥臼。程序可以通過判斷引用隊列中是否已經(jīng)加入了虛引用,來了解被引用的對象是否將要被垃圾回收
ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
利用軟引用和弱引用解決OOM問題:假如有一個應(yīng)用需要讀取大量的本地圖片根灯,如果每次讀取圖片都從硬盤讀取径缅,則會嚴重影響性能,但是如果全部加載到內(nèi)存當中烙肺,又有可能造成內(nèi)存溢出纳猪,此時使用軟引用可以解決這個問題。
十七.java NIO與BIO的區(qū)別
NIO:同步非阻塞io模式桃笙,核心通過selector管理多個連接氏堤,達到一個線程處理多個事件
BIO:同步阻塞IO模式,數(shù)據(jù)的讀取寫入必須阻塞在一個線程內(nèi)等待其完成搏明。
十八.同步阻塞鼠锈、同步非阻塞、異步的區(qū)別
異步:當一個異步過程調(diào)用發(fā)出后星著,調(diào)用者不會立即得到結(jié)果购笆。而是在“發(fā)出后”,“被調(diào)用者“通過狀態(tài)虚循,來通知調(diào)用者同欠,或通過回調(diào)函數(shù)處理這個調(diào)用
同步阻塞:調(diào)用者發(fā)出請求后會一直等待結(jié)果
同步非阻塞:調(diào)用者發(fā)出請求后就去執(zhí)行其他任務(wù)样傍,過一會再詢問被調(diào)用者執(zhí)行結(jié)果
十九.JVM內(nèi)存模型
根據(jù)java虛擬機規(guī)范,java虛擬機管理的內(nèi)存將分為下面五大區(qū)域
1.程序計數(shù)器是一塊很小的內(nèi)存空間铺遂,它是線程私有的衫哥,可以認作為當前線程的行號指示器。
2.線程私有棧描述的是Java方法執(zhí)行的內(nèi)存模型
3.本地方法棧是與虛擬機棧發(fā)揮的作用十分相似,區(qū)別是虛擬機棧執(zhí)行的是Java方法襟锐,而本地方法棧則為虛擬機使用到的native方法服務(wù)炕檩,也是線程私有
4.堆是java虛擬機管理內(nèi)存最大的一塊內(nèi)存區(qū)域,因為堆存放的對象是線程共享的捌斧,所以多線程的時候也需要同步機制
5.方法區(qū)同堆一樣笛质,是所有線程共享的內(nèi)存區(qū)域,用于存儲已被虛擬機加載的類信息捞蚂、常量妇押、靜態(tài)變量,如static修飾的變量加載類的時候就被加載到方法區(qū)中姓迅。
二十.為什么要劃分成年輕代和老年代敲霍,年輕代為什么被劃分成eden、survivor區(qū)域
年輕代用來存放新近創(chuàng)建的對象丁存,老年代中存放的對象是存活了很久的肩杈。
每次新生代的使用,會是eden區(qū)和一塊survivor區(qū)解寝。當我們進行垃圾回收的時候扩然,清除正在使用的區(qū)域,將其中的存貨對象聋伦,
放入到另一個survivor區(qū)域夫偶,并進行整理,保證空間的連續(xù)觉增。如果對象長時間存活兵拢,則將對象移動到老年區(qū)
二十一.java動態(tài)代理和cglib動態(tài)代理的區(qū)別
java動態(tài)代理是利用反射機制生成一個實現(xiàn)代理接口的匿名類,在調(diào)用具體方法前調(diào)用InvokeHandler來處理逾礁。JDK動態(tài)代理只能對實現(xiàn)了接口的類生成代理说铃,而不能針對類
cglib動態(tài)代理是利用asm開源包,對代理對象類的class文件加載進來嘹履,通過修改其字節(jié)碼生成子類來處理腻扇。CGLIB是針對類實現(xiàn)代理,主要是對指定的類生成一個子類植捎,覆蓋其中的方法
二十二.spring中bean的生命周期是怎樣的
- 實例化一個Bean衙解,也就是我們通常說的new
- 按照Spring上下文對實例化的Bean進行配置,也就是IOC注入
- 如果這個Bean實現(xiàn)了BeanNameAware接口焰枢,會調(diào)用它實現(xiàn)的setBeanName(String beanId)方法蚓峦,此處傳遞的是Spring配置文件中Bean的ID
- 如果這個Bean實現(xiàn)了BeanFactoryAware接口舌剂,會調(diào)用它實現(xiàn)的setBeanFactory(),傳遞的是Spring工廠本身(可以用這個方法獲取到其他Bean)
- 如果這個Bean實現(xiàn)了ApplicationContextAware接口暑椰,會調(diào)用setApplicationContext(ApplicationContext)方法霍转,傳入Spring上下文,該方式同樣可以實現(xiàn)步驟4一汽,但比4更好避消,以為ApplicationContext是BeanFactory的子接口,有更多的實現(xiàn)方法
- 如果這個Bean關(guān)聯(lián)了BeanPostProcessor接口召夹,將會調(diào)用postProcessBeforeInitialization(Object obj, String s)方法岩喷,BeanPostProcessor經(jīng)常被用作是Bean內(nèi)容的更改,并且由于這個是在Bean初始化結(jié)束時調(diào)用After方法监憎,也可用于內(nèi)存或緩存技術(shù)
- 如果這個Bean在Spring配置文件中配置了init-method屬性會自動調(diào)用其配置的初始化方法
- 如果這個Bean關(guān)聯(lián)了BeanPostProcessor接口纱意,將會調(diào)用postAfterInitialization(Object obj, String s)方法
注意:以上工作完成以后就可以用這個Bean了,那這個Bean是一個single的鲸阔,所以一般情況下我們調(diào)用同一個ID的Bean會是在內(nèi)容地址相同的實例 - 當Bean不再需要時偷霉,會經(jīng)過清理階段,如果Bean實現(xiàn)了DisposableBean接口褐筛,會調(diào)用其實現(xiàn)的destroy方法
- 最后类少,如果這個Bean的Spring配置中配置了destroy-method屬性,會自動調(diào)用其配置的銷毀方法
二十三.屬性注入和構(gòu)造器注入哪種會有循環(huán)依賴的問題
Spring容器對構(gòu)造函數(shù)配置Bean進行實例化有一個前提渔扎,即Bean構(gòu)造函數(shù)入?yún)⒁玫膶ο蟊仨氁呀?jīng)準備就緒硫狞。
由于這個機制,如果兩個Bean都相互引用赞警,都采用構(gòu)造函數(shù)注入方式妓忍,就會發(fā)生類似于線程死鎖的循環(huán)依賴問題。
可以指定一個bean設(shè)置懶加載屬性
二十四.redis性能為什么高
1.完全基于內(nèi)存愧旦,絕大部分請求是純粹的內(nèi)存操作
2.數(shù)據(jù)存在內(nèi)存中,類似于HashMap定罢,HashMap的優(yōu)勢就是查找和操作的時間復(fù)雜度都是O(1)
3.采用單線程笤虫,避免了不必要的上下文切換和競爭條件,也不存在多進程或者多線程導(dǎo)致的切換而消耗 CPU
4.使用多路I/O復(fù)用模型祖凫,非阻塞IO琼蚯。
二十五.單線程的redis如何利用多核cpu機器
redis的單一線程只能用到一個cpu核心,所以可以在同一個多核的服務(wù)器中惠况,可以啟動多個實例遭庶,組成master-master或者master- slave的形式
二十六.redis的緩存淘汰策略
1.定時過期:每個設(shè)置過期時間的key都需要創(chuàng)建一個定時器,到過期時間就會立即清除稠屠。該策略可以立即清除過期的數(shù)據(jù),對內(nèi)存很友好峦睡;但是會占用大量的CPU資源去處理過期的數(shù)據(jù)
2.惰性過期:只有當訪問一個key時翎苫,才會判斷該key是否已過期,過期則清除榨了。該策略可以最大化地節(jié)省CPU資源煎谍,卻對內(nèi)存非常不友好。極端情況可能出現(xiàn)大量的過期key沒有再次被訪問龙屉,從而不會被清除呐粘,占用大量內(nèi)存。
3.定期過期:每隔一定的時間转捕,會掃描一定數(shù)量的數(shù)據(jù)庫的expires字典中一定數(shù)量的key作岖,并清除其中已過期的key。expires字典會保存所有設(shè)置了過期時間的key的過期時間數(shù)據(jù)
二十七.有海量key和value都比較小的數(shù)據(jù)五芝,在redis中如何存儲才更省內(nèi)存
原理:通過大幅減少key的數(shù)量來降低內(nèi)存的消耗
實現(xiàn):hash,在客戶端通過分組將海量的key根據(jù)一定的策略映射到一組hash對象中
二十八.如何保證redis和DB中的數(shù)據(jù)一致性
1.讀的時候先訪問緩存痘儡,緩存沒有訪問數(shù)據(jù)庫,然后插入緩存
2.寫的時候先寫數(shù)據(jù)庫与柑,再寫到緩存
二十九.如何解決緩存穿透和緩存雪崩
緩存穿透是指查詢一個一定不存在的數(shù)據(jù)谤辜,由于緩存不命中,接著查詢數(shù)據(jù)庫也無法查詢出結(jié)果价捧,因此也不會寫入到緩存中丑念,這將會導(dǎo)致每個查詢都會去請求數(shù)據(jù)庫,造成緩存穿透
解決:當存儲層不命中后结蟋,即使返回的空對象也將其緩存起來脯倚,同時會設(shè)置一個過期時間,之后再訪問這個數(shù)據(jù)將會從緩存中獲取嵌屎,保護了后端數(shù)據(jù)源推正;
緩存雪崩是指,由于緩存層承載著大量請求宝惰,有效的保護了存儲層植榕,但是如果緩存層由于某些原因整體不能提供服務(wù),于是所有的請求都會達到存儲層尼夺,存儲層的調(diào)用量會暴增尊残,造成存儲層也會掛掉的情況。
解決:使用redis集群部署淤堵;對緩存數(shù)據(jù)設(shè)置過期時間增加隨機因子寝衫,防止緩存同時集體失效
三十.redis如何實現(xiàn)分布式鎖
1.分布式鎖需要解決的問題
互斥性:任意時刻只能有一個客戶端擁有鎖,不能同時多個客戶端獲取
安全性:鎖只能被持有該鎖的用戶刪除拐邪,而不能被其他用戶刪除
死鎖:獲取鎖的客戶端因為某些原因而宕機慰毅,而未能釋放鎖,其他客戶端無法獲取此鎖扎阶,需要有機制來避免該類問題的發(fā)生
容錯:當部分節(jié)點宕機汹胃,客戶端仍能獲取鎖或者釋放鎖
2.通過setNX婶芭,并設(shè)置失效時間,在finally塊釋放鎖
三十一.Mysql(innondb) 有哪幾種事務(wù)隔離級別
1.read uncommitted(讀取未提交數(shù)據(jù)):即便是事務(wù)沒有commit统台,但是我們?nèi)匀荒茏x到未提交的數(shù)據(jù)雕擂,級別最低,造成臟讀
2.read committed(可以讀取其他事務(wù)提交的數(shù)據(jù)):當前會話只能讀取到其他事務(wù)提交的數(shù)據(jù)贱勃,未提交的數(shù)據(jù)讀不到井赌。會造成不可重復(fù)讀
3.repeatable read(可重讀)---MySQL默認的隔離級別:當前會話可以重復(fù)讀,就是每次讀取的結(jié)果集都相同贵扰,而不管其他事務(wù)有沒有提交仇穗。會造成幻讀
4.serializable(串行化):其他會話對該表的寫操作將被掛起,隔離級別中最嚴格的戚绕,但是這樣做勢必對性能造成影響
三十二.mysql的行鎖纹坐、表鎖、間隙鎖舞丛、意向鎖分別是做什么的
行鎖:mysql的行鎖是通過索引加載的耘子,即是行鎖是加在索引響應(yīng)的行上的,要是對應(yīng)的SQL語句沒有走索引球切,則會全表掃描
表鎖:就是對沒有索引字段上進行事務(wù)操作谷誓,會導(dǎo)致表鎖
間隙鎖:當我們用范圍條件而不是相等條件檢索數(shù)據(jù),InnoDB會給符合條件的已有數(shù)據(jù)記錄的索引項加鎖吨凑;對于鍵值在條件范圍內(nèi)并不存在的記錄捍歪,叫做間隙
意向鎖:意向鎖產(chǎn)生的主要目的是為了處理行鎖和表鎖之間的沖突
三十三.mysql的讀鎖和寫鎖
MySQL的表級鎖有兩種模式:讀鎖和寫鎖。讀的時候可以讀鸵钝,讀的時候不能寫糙臼,寫的時候不能讀,寫的時候不能寫(讀就是共享讀恩商,其他的可以讀变逃,不能寫,寫是獨立寫怠堪,其他的不能讀也不能寫)
三十四.mysql的最左索引匹配原則
最左優(yōu)先韧献,以最左邊的為起點任何連續(xù)的索引都能匹配上。同時遇到范圍查詢(>研叫、<、between璧针、like)就會停止匹配嚷炉。
三十五.mysql如何優(yōu)化慢查詢
1.索引沒起作用的情況:
在使用LIKE關(guān)鍵字進行查詢的查詢語句中,如果匹配字符串的第一個字符為“%”探橱,索引不會起作用申屹。只有“%”不在第一個位置索引才會起作用绘证。
滿足最左匹配原則
2.優(yōu)化數(shù)據(jù)庫結(jié)構(gòu)
將字段很多的表分解成多個表
對于需要經(jīng)常聯(lián)合查詢的表,可以建立中間表以提高查詢效率
3.分解關(guān)聯(lián)查詢
將一個大的查詢分解為多個小查詢是很有必要的哗讥。
4.優(yōu)化LIMIT分頁
select id,title from collect limit 90000,10; -> select id,title from collect where id>=(select id from collect order by id limit 90000,1) limit 10;
三十六.分庫分表如何選擇分表鍵
1.通過redis維護當前最大id返回
2.可以通過設(shè)置數(shù)據(jù)庫 sequence 或者表的自增字段步長來進行水平伸縮嚷那。每個服務(wù)的初始ID不同
3.UUID,因為其無序性杆煞,導(dǎo)致索引B+樹不能連續(xù)維護索引魏宽,降低效率
4.當前系統(tǒng)時間戳,高并發(fā)情況下不理想
5.snowflake 算法:snowflake 算法是 twitter 開源的分布式 id 生成算法决乎,采用Scala語言實現(xiàn)队询,是把一個64位的long型的id,1個 bit是不用的+用其中的41bit作為毫秒數(shù)+用10bit作為工作機器id+12bi作為序列號构诚。
三十七.分庫分表的情況下蚌斩,查詢時一般是如何做排序的
借鑒三十六.5