HashMap的原理 :
簡單地說,HashMap 在底層將 key-value 當成一個總體進行處理,這個總體就是一個 Entry 對象。HashMap 底層採用一個 Entry[] 數組來保存全部的 key-value 對,當須要存儲一個 Entry 對象時,會根據hash算法來決定其在數組中的存儲位置,在根據equals方法決定其在該數組位置上的鏈表中的存儲位置;當須要取出一個Entry時,
也會根據hash算法找到其在數組中的存儲位置,再根據equals方法從該位置上的鏈表中取出該Entry。
簡單來講,HashMap由數組+鏈表組成的,數組是HashMap的主體,鏈表則是主要爲了解決哈希衝突而存在的,若是定位到的數組位置不含鏈表
(當前entry的next指向null),那麼對於查找,添加等操做很快,僅需一次尋址便可;若是定位到的數組包含鏈表,對於添加操做,其時間複雜度依然爲O(1),
由於最新的Entry會插入鏈表頭部,急須要簡單改變引用鏈便可,而對於查找操做來說,此時就須要遍歷鏈表,而後經過key對象的equals方法逐一比對查找。
因此,性能考慮,HashMap中的鏈表出現越少,性能纔會越好。
簡單地說,HashMap 在底層將 key-value 當成一個總體進行處理,這個總體就是一個 Entry 對象。HashMap 底層採用一個 Entry[] 數組來保存全部的
key-value 對,當須要存儲一個 Entry 對象時,會根據hash算法來決定其在數組中的存儲位置,在根據equals方法決定其在該數組位置上的鏈表中的存儲位置;
當須要取出一個Entry時,也會根據hash算法找到其在數組中的存儲位置,再根據equals方法從該位置上的鏈表中取出該Entry。
HashMap的底層經過位桶實現,位桶裏面存的是鏈表(1.7之前)或者紅黑樹(有序,1.8開始) ,其實就是數組加鏈表(或者紅黑樹)的格式,
經過判斷hashCode定位位桶中的下標,經過equals定位目標值在鏈表中的位置,因此若是你使用的key使用可變類(非final修飾的類),
那麼你在自定義hashCode和equals的時候必定要注意要知足:若是兩個對象equals那麼必定要hashCode相同,若是是hashCode相同的話不必定要求equals!
因此通常來講不要自定義hashCode和equls,推薦使用不可變類對象作key,好比Integer、String等等。java
ConcurrentHashMap的原理 :
ConcurrentHashMap的鎖分段技術
HashTable容器在競爭激烈的併發環境下表現出效率低下的緣由,是由於全部訪問HashTable的線程都必須競爭同一把鎖,那假如容器裏有多把鎖,
每一把鎖用於鎖容器其中一部分數據,那麼當多線程訪問容器裏不一樣數據段的數據時,線程間就不會存在鎖競爭,從而能夠有效的提升併發訪問效率,
這就是ConcurrentHashMap所使用的鎖分段技術,首先將數據分紅一段一段的存儲,而後給每一段數據配一把鎖,當一個線程佔用鎖訪問其中一個段數據的時候,
其餘段的數據也能被其餘線程訪問。
ConcurrentHashMap是由Segment數組結構和HashEntry數組結構組成。Segment是一種可重入鎖ReentrantLock,在ConcurrentHashMap裏扮演鎖的角色,
HashEntry則用於存儲鍵值對數據。一個ConcurrentHashMap裏包含一個Segment數組,Segment的結構和HashMap相似,是一種數組和鏈表結構,
一個Segment裏包含一個HashEntry數組,每一個HashEntry是一個鏈表結構的元素, 每一個Segment守護者一個HashEntry數組裏的元素,
當對HashEntry數組的數據進行修改時,必須首先得到它對應的Segment鎖。算法
Sleep和wait() :
sleep 是線程類(Thread)的方法,致使此線程暫停執行指定時間,給執行機會給其餘線程,可是監控狀態依然保持,到時後會自動恢復,調用sleep 不會釋放對象鎖。因爲沒有釋放對象鎖,因此不能調用裏面的同步方法。編程
sleep()使當前線程進入停滯狀態(阻塞當前線程),讓出CUP的使用、目的是不讓當前線程獨自霸佔該進程所獲的CPU資源,以留必定時間給其餘線程執行的機會;
sleep()是Thread類的Static(靜態)的方法;所以他不能改變對象的機鎖,因此當在一個Synchronized塊中調用Sleep()方法是,線程雖然休眠了,可是對象的機鎖並木有被釋放,其餘線程沒法訪問這個對象(即便睡着也持有對象鎖)。
在sleep()休眠時間期滿後,該線程不必定會當即執行,這是由於其它線程可能正在運行並且沒有被調度爲放棄執行,除非此線程具備更高的優先級。數組
wait()方法是Object類裏的方法;當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時失去(釋放)了對象的機鎖(暫時失去機鎖,wait(long timeout)超時時間到後還須要返還對象鎖);能夠調用裏面的同步方法,其餘線程能夠訪問;
wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當前等待池中的線程。
wiat()必須放在synchronized block中,不然會在program runtime時扔出」java.lang.IllegalMonitorStateException「異常。多線程
二
sleep必須捕獲異常,而wait,notify和notifyAll不須要捕獲異常併發
sleep方法屬於Thread類中方法,表示讓一個線程進入睡眠狀態,等待必定的時間以後,自動醒來進入到可運行狀態,不會立刻進入運行狀態,由於線程調度機制恢復線程的運行也須要時間,一個線程對象調用了sleep方法以後,並不會釋放他所持有的全部對象鎖,因此也就不會影響其餘進程對象的運行。但在sleep的過程當中過程當中有可能被其餘對象調用它的interrupt(),產生InterruptedException異常,若是你的程序不捕獲這個異常,線程就會異常終止,進入TERMINATED狀態,若是你的程序捕獲了這個異常,那麼程序就會繼續執行catch語句塊(可能還有finally語句塊)以及之後的代碼。函數
注意sleep()方法是一個靜態方法,也就是說他只對當前對象有效,經過t.sleep()讓t對象進入sleep,這樣的作法是錯誤的,它只會是使當前線程被sleep 而不是t線程高併發
wait屬於Object的成員方法,一旦一個對象調用了wait方法,必需要採用notify()和notifyAll()方法喚醒該進程;若是線程擁有某個或某些對象的同步鎖,那麼在調用了wait()後,這個線程就會釋放它持有的全部同步資源,而不限於這個被調用了wait()方法的對象。wait()方法也一樣會在wait的過程當中有可能被其餘對象調用interrupt()方法而產生性能
三
這二者的施加者是有本質區別的.
sleep()是讓某個線程暫停運行一段時間,其控制範圍是由當前線程決定,也就是說,在線程裏面決定.比如如說,我要作的事情是 "點火->燒水->煮麪",而當我點完火以後我不當即燒水,我要休息一段時間再燒.對於運行的主動權是由個人流程來控制.this
而wait(),首先,這是由某個肯定的對象來調用的,將這個對象理解成一個傳話的人,當這我的在某個線程裏面說"暫停!",也是 thisOBJ.wait(),這裏的暫停是阻塞,仍是"點火->燒水->煮飯",thisOBJ就比如一個監督個人人站在我旁邊,原本該線 程應該執行1後執行2,再執行3,而在2處被那個對象喊暫停,那麼我就會一直等在這裏而不執行3,但正個流程並無結束,我一直想去煮飯,但還沒被容許, 直到那個對象在某個地方說"通知暫停的線程啓動!",也就是thisOBJ.notify()的時候,那麼我就能夠煮飯了,這個被暫停的線程就會從暫停處 繼續執行.
其實二者均可以讓線程暫停一段時間,可是本質的區別是一個線程的運行狀態控制,一個是線程之間的通信的問題
在java.lang.Thread類中,提供了sleep(),
而java.lang.Object類中提供了wait(), notify()和notifyAll()方法來操做線程
sleep()能夠將一個線程睡眠,參數能夠指定一個時間。
而wait()能夠將一個線程掛起,直到超時或者該線程被喚醒。
wait有兩種形式wait()和wait(milliseconds).
sleep和wait的區別有:
1,這兩個方法來自不一樣的類分別是Thread和Object
2,最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其餘線程可使用同步控制塊或者方法。
3,wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep能夠在
任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
4,sleep必須捕獲異常,而wait,notify和notifyAll不須要捕獲異常
一、Java都有哪些鎖?
公平鎖/非公平鎖
可重入鎖
獨享鎖/共享鎖
互斥鎖/讀寫鎖
樂觀鎖/悲觀鎖
分段鎖
偏向鎖/輕量級鎖/重量級鎖
自旋鎖
Java實現鎖有兩種語法,一種是synchronized語句,另一種是reentrantlock關鍵字。上面是不少鎖的名詞,這些分類並非全是指鎖的狀態,有的指鎖的特性,有的指鎖的設計,下面總結的內容是對每一個鎖的名詞進行必定的解釋。
公平鎖/非公平鎖
公平鎖指多個線程按照申請鎖的順序得到鎖。
非公平鎖指多個線程得到鎖的順序不按照申請順序。
Java reentranthlock經過構造函數來指定鎖是公平仍是非公平,默認是非公平鎖,對於synchronized而言,也是一種非公平鎖。
非公平鎖優勢在於吞吐量比公平鎖大。
可重入鎖
可重入鎖又叫遞歸鎖,是指同一個線程在外層方法獲取鎖的時候,在進入內層方法會自動獲取鎖。舉例以下:
synchronized void setA() throws Exception{
Thread.sleep(1000);
setB();
}
synchronized void setB() throws Exception{
Thread.sleep(1000);
}
Java reentrantlock是一個可重入鎖。(上面的代碼就是一個可重入鎖的一個特色,若是不是可重入鎖的話,setB可能不會被當前線程執行,可能形成死鎖)
Synchronized也是一個可重入鎖。
可重入鎖的優勢是能夠必定程度避免死鎖。
獨享鎖/共享鎖
顧名思義,獨享鎖是指該鎖一次只能被一個線程所持有,共享鎖能夠被多個線程所持有。
Java reentrantlock是一個獨享鎖,可是對於lock的另外一個實現readwritelock,其讀鎖是一個共享鎖,寫鎖是一個獨享鎖。
對於synchronized是一個獨享鎖。
互斥鎖/讀寫鎖
上邊說的獨享鎖和共享鎖是一種廣義的說法,互斥鎖和讀寫鎖就是具體實現。
互斥鎖在Java中具體實現就是reentrantlock。
讀寫鎖在Java中的具體實現就是readwritelock。
樂觀鎖/悲觀鎖
樂觀鎖和悲觀鎖不是指具體的鎖類型,而是對於看待併發編程中加鎖問題的角度。
悲觀鎖認爲,對於一個數據的併發操做,必定會改變數據,即便實際上數據沒被改變,可是也悲觀的認爲被改變的可能性比較大,必定要加鎖,不加鎖遲早要出問題。
樂觀鎖認爲,對於一個數據的併發操做,是不會改變數據的,不加鎖也不會出問題。
樂觀鎖指java中的無所編程,適合讀操做很是多的場景。
悲觀鎖就是指java中,適合併發下寫很是多的場景。
自旋鎖
在java中,自旋鎖是指常識獲取鎖的線程不會當即阻塞,而是採用循環的方式去嘗試獲取鎖,當循環條件被其餘線程改變時,才能進入臨界區。
這樣的好處是減小線程上下文切換的消耗,缺點是會消耗CPU。