Java8張圖 1
1佩迟、字符串不變性 1
2溃肪、equals()方法、hashCode()方法的區(qū)別 1
3音五、Java異常類的層次結(jié)構(gòu) 1
4惫撰、集合類的層次結(jié)構(gòu) 2
5、Java同步 3
6躺涝、別名 3
7厨钻、堆和棧 3
8、Java虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)域 3
一坚嗜、Java基礎(chǔ) 4
1夯膀、java中的Synchronized實(shí)現(xiàn) http://blog.csdn.net/hsuxu/article/details/9472371 4
2、hashmap面試問題集 http://blog.csdn.net/song19890528/article/details/16891015 6
3苍蔬、Java中的有些鎖 http://www.cnblogs.com/qq78292959/p/4252800.html 9
4诱建、Hashset為什么不加重復(fù)的值 http://blog.sina.com.cn/s/blog_69fca0a20100knsh.html 12
5、hashmap實(shí)現(xiàn)原理淺析 http://www.cnblogs.com/lzrabbit/p/3721067.html 13
6碟绑、如何理解 Java反射機(jī)制 http://blog.csdn.net/nemo2011/article/details/6585683 14
7俺猿、什么是動(dòng)態(tài)代理 http://huangnx.com/2016/10/17/proxyDesignDesc/ 14
8、JavaCAS原理深度分析 http://blog.csdn.net/hsuxu/article/details/9467651 14
9格仲、 Java類加載機(jī)制 http://blog.csdn.net/love_Javc_you/article/details/38081683 18
10押袍、 JDK7與JDK8中HashMap的實(shí)現(xiàn) https://my.oschina.net/hosee/blog/618953 19
11、 Java中常用的鎖分析總結(jié) http://www.tuicool.com/articles/NnQjyq 19
12凯肋、volatile與synchronized的區(qū)別 http://blog.csdn.net/fanaticism1/article/details/9966163 20
13谊惭、 Java中的object類有哪些方法 : 21
14、 深復(fù)制與淺復(fù)制的區(qū)別 21
15侮东、 深入剖析Java中的裝箱和拆箱 http://www.cnblogs.com/dolphin0520/p/3780005.html 21
17圈盔、java8的新特性: 22
18、 Java動(dòng)態(tài)綁定總結(jié) http://www.cnblogs.com/lyp3314/archive/2013/01/26/2877205.html 23
19悄雅、 Java堆和棧詳解 http://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html 23
16驱敲、 JDK 源代碼研究 Hash 存儲機(jī)制 http://www.ibm.com/developerworks/cn/java/j-lo-hash/ 24
17、 JVM內(nèi)存管理煤伟, 新生代癌佩, 舊生代 http://blog.sina.com.cn/s/blog_55ba8b4601014nzm.html 24
18、 圖解Tomcat 與jvm 類加載機(jī)制 http://www.cnblogs.com/xing901022/p/4574961.html 26
19便锨、 Java異常處理和設(shè)計(jì) http://www.cnblogs.com/dolphin0520/p/3769804.html 27
20围辙、 B樹、B-樹放案、B+樹姚建、B*樹 http://www.cnblogs.com/oldhorse/archive/2009/11/16/1604009.html 27
21、 死鎖產(chǎn)生的原因和如何解決吱殉? 30
22掸冤、如何檢測兩個(gè)線程死鎖厘托,或者說找出死鎖的線程 30
23、進(jìn)程與線程的區(qū)別和聯(lián)系稿湿,進(jìn)程執(zhí)行的方式铅匹。 31
24、jvm虛擬機(jī)溢出都是發(fā)生在哪個(gè)地方饺藤?什么情況下會發(fā)生溢出包斑?原因何在? 31
25涕俗、equals罗丰、==、hashcode http://blog.csdn.net/hla199106/article/details/46907725 33
二再姑、 三大架構(gòu)問題 35
1萌抵、 Spring事務(wù)傳播機(jī)制和數(shù)據(jù)庫隔離級別 http://comedsh.iteye.com/blog/698733 35
2、 Spring工廠模式和單態(tài)模式 http://blog.csdn.net/titilover/article/details/6729058 36
3元镀、 理解IoC和DI绍填、AOP 38
4、 動(dòng)態(tài)代理 http://huangnx.com/2016/10/17/proxyDesignDesc/ 38
5凹联、 Struts2的工作機(jī)制沐兰? 38
6哆档、 Hibernate與Mybaits的區(qū)別蔽挠? 38
7、 Hibernate如何實(shí)現(xiàn)級聯(lián)操作瓜浸? 39
8澳淑、 Spring有哪些優(yōu)點(diǎn)?為什么要用spring插佛?(總結(jié)幾點(diǎn)就好) 39
9杠巡、http的get和post區(qū)別,servlet都有哪些方法雇寇?service有哪些方法氢拥? 40
三、 數(shù)據(jù)庫問題 41
1锨侯、SQL中char嫩海、varchar、nvarchar的區(qū)別 41
2囚痴、事務(wù)是什么,以及事務(wù)四個(gè)特性 42
3叁怪、什么情況下需要?jiǎng)?chuàng)建MySQL索引 http://www.jb51.net/article/56532.htm 42
4、如何優(yōu)化SQL語句深滚? 43
5奕谭、數(shù)據(jù)庫事務(wù)隔離級別和鎖實(shí)現(xiàn)機(jī)制 http://comedsh.iteye.com/blog/698733 43
6涣觉、SQL Select語句完整的執(zhí)行順序: 43
7、聚簇索引和非聚簇索引的區(qū)別血柳?(具體可百度) 44
四官册、 雜七雜八 44
1、什么是單工难捌、半雙工攀隔、全雙工? 44
2栖榨、三次握手昆汹、四次揮手的原理? 44
3婴栽、http與https的區(qū)別 http://www.mahaixiang.cn/internet/1233.html 44
Java8張圖
https://blog.csdn.net/zivensonice/article/details/51465531
1满粗、字符串不變性
下面這張圖展示了這段代碼做了什么
String s = "abcd";
s = s.concat("ef");
2、equals()方法愚争、hashCode()方法的區(qū)別
HashCode被設(shè)計(jì)用來提高性能映皆。equals()方法與hashCode()方法的區(qū)別在于:
1)如果兩個(gè)對象相等(equal),那么他們一定有相同的哈希值轰枝。
2)如果兩個(gè)對象的哈希值相同捅彻,但他們未必相等(equal)。
3鞍陨、Java異常類的層次結(jié)構(gòu)
圖中紅色部分為受檢查異常步淹。它們必須被捕獲,或者在函數(shù)中聲明為拋出該異常诚撵。
4缭裆、集合類的層次結(jié)構(gòu)
注意Collections和Collection的區(qū)別。(Collections包含有各種有關(guān)集合操作的靜態(tài)多態(tài)方法)
5寿烟、Java同步
Java同步機(jī)制可通過類比建筑物來闡明澈驼。
6、別名
別名意味著有多個(gè)變量指向同一可被更新的內(nèi)存塊筛武,
這些別名分別是不同的對象類型缝其。
7、堆和棧
圖解表明了方法和對象在運(yùn)行時(shí)內(nèi)存中的位置徘六。
8内边、Java虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)域
圖解展示了整個(gè)虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)域的情況。
一硕噩、Java基礎(chǔ)
1假残、java中的Synchronized實(shí)現(xiàn) http://blog.csdn.net/hsuxu/article/details/9472371
(1)同步的基礎(chǔ):
Java中的每一個(gè)對象都可以作為鎖。
? 對于同步方法,鎖是當(dāng)前實(shí)例對象。
? 對于靜態(tài)同步方法,鎖是當(dāng)前對象的Class對象泣矛。
? 對于同步方法塊测蹲,鎖是Synchonized括號里配置的對象。
當(dāng)一個(gè)線程試圖訪問同步代碼塊時(shí),它首先必須得到鎖,退出或拋出異常時(shí)必須釋放鎖。那么鎖存在哪里呢纲岭?鎖里面會存儲什么信息呢?
(2)同步的原理
JVM規(guī)范規(guī)定JVM基于進(jìn)入和退出Monitor對象來實(shí)現(xiàn)方法同步和代碼塊同步线罕,但兩者的實(shí)現(xiàn)細(xì)節(jié)不一樣止潮。代碼塊同步是使用monitorenter和monitorexit指令實(shí)現(xiàn),而方法同步是使用另外一種方式實(shí)現(xiàn)的钞楼,細(xì)節(jié)在JVM規(guī)范里并沒有詳細(xì)說明喇闸,但是方法的同步同樣可以使用這兩個(gè)指令來實(shí)現(xiàn)。monitorenter指令是在編譯后插入到同步代碼塊的開始位置询件,而monitorexit是插入到方法結(jié)束處和異常處燃乍, JVM要保證每個(gè)monitorenter必須有對應(yīng)的monitorexit與之配對。任何對象都有一個(gè) monitor 與之關(guān)聯(lián)宛琅,當(dāng)且一個(gè)monitor 被持有后刻蟹,它將處于鎖定狀態(tài)。線程執(zhí)行到 monitorenter 指令時(shí)嘿辟,將會嘗試獲取對象所對應(yīng)的 monitor 的所有權(quán)舆瘪,即嘗試獲得對象的鎖。
(3) Java對象頭
鎖存在Java對象頭里仓洼。如果對象是數(shù)組類型介陶,則虛擬機(jī)用3個(gè)Word(字寬)存儲對象頭,如果對象是非數(shù)組類型色建,則用2字寬存儲對象頭。在32位虛擬機(jī)中舌缤,一字寬等于四字節(jié)箕戳,即32bit。
長度 內(nèi)容 說明
32/64bit Mark Word 存儲對象的hashCode或鎖信息等国撵。
32/64bit Class Metadata Address 存儲到對象類型數(shù)據(jù)的指針
32/64bit Array length 數(shù)組的長度(如果當(dāng)前對象是數(shù)組)
Java對象頭里的Mark Word里默認(rèn)存儲對象的HashCode陵吸,分代年齡和鎖標(biāo)記位。32位JVM的Mark Word的默認(rèn)存儲結(jié)構(gòu)如下:
25 bit 4bit 1bit
是否是偏向鎖 2bit
鎖標(biāo)志位
無鎖狀態(tài) 對象的hashCode 對象分代年齡 0 01
在運(yùn)行期間Mark Word里存儲的數(shù)據(jù)會隨著鎖標(biāo)志位的變化而變化介牙。Mark Word可能變化為存儲以下4種數(shù)據(jù):
鎖狀態(tài) 25 bit 4bit 1bit 2bit
23bit 2bit 是否是偏向鎖 鎖標(biāo)志位
輕量級鎖 指向棧中鎖記錄的指針 00
重量級鎖 指向互斥量(重量級鎖)的指針 10
GC標(biāo)記 空 11
偏向鎖 線程ID Epoch 對象分代年齡 1 01
在64位虛擬機(jī)下壮虫,Mark Word是64bit大小的,其存儲結(jié)構(gòu)如下:
鎖狀態(tài) 25bit 31bit 1bit 4bit 1bit 2bit
cms_free 分代年齡 偏向鎖 鎖標(biāo)志位
無鎖 unused hashCode 0 01
偏向鎖 ThreadID(54bit) Epoch(2bit) 1 01
(4)鎖的升級
Java SE1.6為了減少獲得鎖和釋放鎖所帶來的性能消耗,引入了“偏向鎖”和“輕量級鎖”囚似,所以在Java SE1.6里鎖一共有四種狀態(tài)剩拢,無鎖狀態(tài),偏向鎖狀態(tài)饶唤,輕量級鎖狀態(tài)和重量級鎖狀態(tài)徐伐,它會隨著競爭情況逐漸升級。鎖可以升級但不能降級募狂,意味著偏向鎖升級成輕量級鎖后不能降級成偏向鎖办素。這種鎖升級卻不能降級的策略,目的是為了提高獲得鎖和釋放鎖的效率祸穷,下文會詳細(xì)分析性穿。
(5)偏向鎖
Hotspot的作者經(jīng)過以往的研究發(fā)現(xiàn)大多數(shù)情況下鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得雷滚,為了讓線程獲得鎖的代價(jià)更低而引入了偏向鎖季二。當(dāng)一個(gè)線程訪問同步塊并獲取鎖時(shí),會在對象頭和棧幀中的鎖記錄里存儲鎖偏向的線程ID揭措,以后該線程在進(jìn)入和退出同步塊時(shí)不需要花費(fèi)CAS操作來加鎖和解鎖胯舷,而只需簡單的測試一下對象頭的Mark Word里是否存儲著指向當(dāng)前線程的偏向鎖,如果測試成功绊含,表示線程已經(jīng)獲得了鎖桑嘶,如果測試失敗,則需要再測試下Mark Word中偏向鎖的標(biāo)識是否設(shè)置成1(表示當(dāng)前是偏向鎖)躬充,如果沒有設(shè)置逃顶,則使用CAS競爭鎖,如果設(shè)置了充甚,則嘗試使用CAS將對象頭的偏向鎖指向當(dāng)前線程以政。
偏向鎖的撤銷:偏向鎖使用了一種等到競爭出現(xiàn)才釋放鎖的機(jī)制,所以當(dāng)其他線程嘗試競爭偏向鎖時(shí)伴找,持有偏向鎖的線程才會釋放鎖盈蛮。偏向鎖的撤銷,需要等待全局安全點(diǎn)(在這個(gè)時(shí)間點(diǎn)上沒有字節(jié)碼正在執(zhí)行)技矮,它會首先暫停擁有偏向鎖的線程抖誉,然后檢查持有偏向鎖的線程是否活著,如果線程不處于活動(dòng)狀態(tài)衰倦,則將對象頭設(shè)置成無鎖狀態(tài)袒炉,如果線程仍然活著,擁有偏向鎖的棧會被執(zhí)行樊零,遍歷偏向?qū)ο蟮逆i記錄我磁,棧中的鎖記錄和對象頭的Mark Word要么重新偏向于其他線程,要么恢復(fù)到無鎖或者標(biāo)記對象不適合作為偏向鎖,最后喚醒暫停的線程夺艰。
關(guān)閉偏向鎖:偏向鎖在Java 6和Java 7里是默認(rèn)啟用的芋哭,但是它在應(yīng)用程序啟動(dòng)幾秒鐘之后才激活,如有必要可以使用JVM參數(shù)來關(guān)閉延遲-XX:BiasedLockingStartupDelay = 0劲适。如果你確定自己應(yīng)用程序里所有的鎖通常情況下處于競爭狀態(tài)楷掉,可以通過JVM參數(shù)關(guān)閉偏向鎖-XX:-UseBiasedLocking=false,那么默認(rèn)會進(jìn)入輕量級鎖狀態(tài)霞势。
(6)輕量級鎖
輕量級鎖加鎖:線程在執(zhí)行同步塊之前烹植,JVM會先在當(dāng)前線程的棧楨中創(chuàng)建用于存儲鎖記錄的空間,并將對象頭中的Mark Word復(fù)制到鎖記錄中愕贡,官方稱為Displaced Mark Word草雕。然后線程嘗試使用CAS將對象頭中的Mark Word替換為指向鎖記錄的指針。如果成功固以,當(dāng)前線程獲得鎖墩虹,如果失敗,表示其他線程競爭鎖憨琳,當(dāng)前線程便嘗試使用自旋來獲取鎖诫钓。
輕量級鎖解鎖:輕量級解鎖時(shí),會使用原子的CAS操作來將Displaced Mark Word替換回到對象頭篙螟,如果成功菌湃,則表示沒有競爭發(fā)生。如果失敗遍略,表示當(dāng)前鎖存在競爭惧所,鎖就會膨脹成重量級鎖。
因?yàn)樽孕龝腃PU绪杏,為了避免無用的自旋(比如獲得鎖的線程被阻塞住了)下愈,一旦鎖升級成重量級鎖,就不會再恢復(fù)到輕量級鎖狀態(tài)蕾久。當(dāng)鎖處于這個(gè)狀態(tài)下势似,其他線程試圖獲取鎖時(shí),都會被阻塞住腔彰,當(dāng)持有鎖的線程釋放鎖之后會喚醒這些線程叫编,被喚醒的線程就會進(jìn)行新一輪的奪鎖之爭。
(7)鎖的優(yōu)缺點(diǎn)對比
鎖 優(yōu)點(diǎn) 缺點(diǎn) 適用場景
偏向鎖 加鎖和解鎖不需要額外的消耗霹抛,和執(zhí)行非同步方法比僅存在納秒級的差距。 如果線程間存在鎖競爭卷谈,會帶來額外的鎖撤銷的消耗杯拐。 適用于只有一個(gè)線程訪問同步塊場景。
輕量級鎖 競爭的線程不會阻塞,提高了程序的響應(yīng)速度端逼。 如果始終得不到鎖競爭的線程使用自旋會消耗CPU朗兵。 追求響應(yīng)時(shí)間。
同步塊執(zhí)行速度非扯ヌ玻快余掖。
重量級鎖 線程競爭不使用自旋,不會消耗CPU礁鲁。 線程阻塞盐欺,響應(yīng)時(shí)間緩慢。 追求吞吐量仅醇。
同步塊執(zhí)行速度較長冗美。
2、hashmap面試問題集 http://blog.csdn.net/song19890528/article/details/16891015
(1)HashMap與哈希表
HashMap從本質(zhì)上說就是哈希表析二,其底層實(shí)現(xiàn)就是圍繞哈希表展看的粉洼。
哈希表的核心思想就是讓記錄的關(guān)鍵字和存儲位置建立一一映射關(guān)系,這樣我們就可以通過Key直接獲得相對應(yīng)的Value叶摄,好比我們通過索引可以直接獲得數(shù)組對應(yīng)的某個(gè)值一樣属韧,而這種一一映射關(guān)系要通過某個(gè)數(shù)學(xué)函數(shù)來構(gòu)造出來,這個(gè)函數(shù)就是所謂的哈希函數(shù)蛤吓。
而哈希函數(shù)有五種實(shí)現(xiàn)方式:
A. 直接定址法:取關(guān)鍵字的線性函數(shù)值作為哈希地址宵喂。
B. 數(shù)字分析法:取關(guān)鍵字的中的若干位作為哈希地址。
C. 平方取中法:取關(guān)鍵字平方后的中間幾位作為哈希地址柱衔。
D. 折疊法:將關(guān)鍵字分割成位數(shù)相同的幾部分(最后一部分可以不同)樊破,然后取這幾部分的疊加和作為哈希地址。
E. 除留余數(shù)法:H(key) = key MOD p 唆铐,p<=m 哲戚,m為不大于哈希表的數(shù)。
F. 隨機(jī)函數(shù)法
上述五中實(shí)現(xiàn)方式中最常用的是除留余數(shù)法艾岂,而通過哈希函數(shù)尋址的過程可能出現(xiàn)“沖突”------即若干個(gè)不同的key卻對應(yīng)相同的哈希地址顺少。解決哈希沖突有如下的方法:
A. 開放地址法:H=(H(kyt)+d) MOD m ,m為哈希表表長王浴。
(1)d=1,2脆炎,3------> m-1 時(shí),稱謂線性探測再散列
(2)d=12氓辣,-12---->+(-)k^2時(shí)秒裕,稱為二次線性再散列。
(3)d為偽隨即序列時(shí)钞啸,稱為偽隨即序列再散列几蜻。
B .再哈希法 :H=RH(key)喇潘,RH()為不同的哈希函數(shù),即在地址沖突時(shí)計(jì)算另一個(gè)哈希函數(shù)地址梭稚,直到不再發(fā)生沖突颖低。
C .鏈地址法:將所有哈希地址沖突的記錄存儲在同一個(gè)線性鏈表中
D 公共溢出區(qū)法:將所有哈希地址沖突的記錄都填入到溢出表中
而HashMap的實(shí)現(xiàn)與哈希函數(shù)的選擇和哈希地址沖突的解決方案密切相關(guān)
(2)HashMap的具體實(shí)現(xiàn)
HashMap的實(shí)現(xiàn)采用了除留余數(shù)法形式的哈希函數(shù)和鏈地址法解決哈希地址沖突的方案。這樣就涉及到兩種基本的數(shù)據(jù)結(jié)構(gòu):數(shù)組和鏈表弧烤。數(shù)組的索引就是對應(yīng)的哈希地址忱屑,存放的是鏈表的頭結(jié)點(diǎn)即插入鏈表中的最后一個(gè)元素,鏈表存放的是哈希地址沖突的不同記錄暇昂。
鏈表的結(jié)點(diǎn)設(shè)計(jì)如下:
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
}
next作為引用指向下一個(gè)記錄莺戒。在HashMap中設(shè)計(jì)了一個(gè)Entry類型的數(shù)組用來存放Entry的實(shí)例即鏈表結(jié)點(diǎn)。
/** The table, resized as necessary. Length MUST Always be a power of two. /
transient Entry[] table;
除留余數(shù)法形式的哈希函數(shù):
/* Returns index for hash code h. */
static int indexFor(int h, int length) { return h & (length-1); //和除留余數(shù)等價(jià) }
當(dāng)我們往HashMap中put元素的時(shí)候话浇,先根據(jù)key的hashCode重新計(jì)算hash值脏毯,根據(jù)hash值得到這個(gè)元素在數(shù)組中的位置(即下標(biāo)),如果數(shù)組該位置上已經(jīng)存放有其他元素了幔崖,那么在這個(gè)位置上的元素將以鏈表的形式存放食店,新加入的放在鏈頭,最先加入的放在鏈尾赏寇,數(shù)組中存儲的是最后插入的元素 吉嫩。如果數(shù)組該位置上沒有元素,就直接將該元素放到此數(shù)組中的該位置上嗅定。
1)HashMap與Hashtable的區(qū)別:
a自娩、HashMap可以接受null鍵值和值,而Hashtable則不能渠退。b忙迁、Hashtable是線程安全的,通過synchronized實(shí)現(xiàn)線程同步碎乃。而HashMap是非線程安全的姊扔,但是速度比Hashtable快。
2)當(dāng)兩個(gè)對象的hashcode相同怎么辦
當(dāng)哈希地址沖突時(shí)梅誓,HashMap采用了鏈地址法的解決方式恰梢,將所有哈希地址沖突的記錄存儲在同一個(gè)線性鏈表中。具體來說就是根據(jù)hash值得到這個(gè)元素在數(shù)組中的位置(即下標(biāo))梗掰,如果數(shù)組該位置上已經(jīng)存放有其他元素了嵌言,那么在這個(gè)位置上的元素將以鏈表的形式存放,新加入的放在鏈頭及穗,最先加入的放在鏈尾摧茴。
3)如果兩個(gè)鍵的hashcode相同,你如何獲取值對象
HashMap在鏈表中存儲的是鍵值對埂陆,找到哈希地址位置之后蓬蝶,會調(diào)用keys.equals()方法去找到鏈表中正確的節(jié)點(diǎn)尘分,最終找到要找的值對象
4)如果HashMap的大小超過了負(fù)載因子(load factor)定義的容量猜惋,怎么辦
HashMap默認(rèn)的負(fù)載因子大小為0.75丸氛,也就是說,當(dāng)一個(gè)map填滿了75%的空間的時(shí)候著摔,和其它集合類(如ArrayList等)一樣缓窜,將會創(chuàng)建原來HashMap大小的兩倍的數(shù)組,來重新調(diào)整map的大小谍咆,并將原來的對象放入新的數(shù)組中禾锤。
5)為什么String, Interger這樣的wrapper類適合作為鍵?
String, Interger這樣的wrapper類是final類型的摹察,具有不可變性恩掷,而且已經(jīng)重寫了equals()和hashCode()方法了。其他的wrapper類也有這個(gè)特點(diǎn)供嚎。不可變性是必要的黄娘,因?yàn)闉榱艘?jì)算hashCode(),就要防止鍵值改變克滴,如果鍵值在放入時(shí)和獲取時(shí)返回不同的hashcode的話逼争,那么就不能從HashMap中找到你想要的對象。
6)ConcurrentHashMap和Hashtable的區(qū)別
Hashtable和ConcurrentHashMap有什么分別呢劝赔?它們都可以用于多線程的環(huán)境誓焦,但是當(dāng)Hashtable的大小增加到一定的時(shí)候,性能會急劇下降着帽,因?yàn)榈鷷r(shí)需要被鎖定很長的時(shí)間杂伟。因?yàn)镃oncurrentHashMap引入了分割(segmentation),不論它變得多么大仍翰,僅僅需要鎖定map的某個(gè)部分赫粥,而其它的線程不需要等到迭代完成才能訪問map。簡而言之歉备,在迭代的過程中傅是,ConcurrentHashMap僅僅鎖定map的某個(gè)部分,而Hashtable則會鎖定整個(gè)map蕾羊。
7)HashMap的遍歷
第一種:
Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
Object val = entry.getValue();
}
效率高,以后一定要使用此種方式喧笔!
第二種:
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
Object key = iter.next();
Object val = map.get(key);
}
效率低,以后盡量少使用!
可是為什么第一種比第二種方法效率更高呢龟再?
HashMap這兩種遍歷方法是分別對keyset及entryset來進(jìn)行遍歷书闸,但是對于keySet其實(shí)是遍歷了2次,一次是轉(zhuǎn)為iterator利凑,一次就從hashmap中取出key所對于的value浆劲。而entryset只是遍歷了第一次嫌术,它把key和value都放到了entry中,即鍵值對牌借,所以就快了度气。
3、Java中的有些鎖 http://www.cnblogs.com/qq78292959/p/4252800.html
1)什么是線程安全膨报?
當(dāng)多個(gè)線程訪問一個(gè)對象時(shí)磷籍,如果不用考慮這些線程在運(yùn)行時(shí)環(huán)境的調(diào)度和交替執(zhí)行,也不需要進(jìn)行額外的同步现柠,或者在調(diào)用方進(jìn)行任何其他的協(xié)調(diào)操作院领,調(diào)用這個(gè)對象的行為都可以獲得正確的結(jié)果,那這個(gè)對象就是線程安全的够吩。代碼本省封裝了所有必要的正確性保障手段(互斥同步等)比然,令調(diào)用者無需關(guān)心多線程的問題,更無需自己實(shí)現(xiàn)任何措施來保證多線程的正確調(diào)用周循。
2)線程之間的交互機(jī)制强法?
不同的線程之間會產(chǎn)生競爭,同樣也有交互鱼鼓,最典型的例如數(shù)據(jù)庫連接池拟烫,一組數(shù)據(jù)庫連接放在一個(gè)數(shù)組中,如果線程需要數(shù)據(jù)庫操作迄本,則從池中獲取鏈接硕淑,用完了就放回去。JVM提供了wair/notify/notifyAll方式來滿足這類需求嘉赎,典型的代碼如下:
package lock;
public class Pool {
public Connection get(){
synchronized (this) {
if(free>0){
free--;
}else{
this.wait();
}
return cacheConnection.poll();
}
}
public void close(Connection conn){
synchronized (this) {
free++;
cacheConnection.offer(conn);
this.notifyAll();
}
}
}
3)如何實(shí)現(xiàn)線程安全置媳?
A、互斥同步公条,最常見的并發(fā)正確性保障手段拇囊,同步至多個(gè)線程并發(fā)訪問共享數(shù)據(jù)時(shí),保證共享數(shù)據(jù)在同一個(gè)時(shí)刻只被一個(gè)線程使用靶橱。
B寥袭、非阻塞同步,互斥同步的主要問題就是進(jìn)行線程的阻塞和喚醒所帶來的性能問題关霸,因此這個(gè)同步也被稱為阻塞同步传黄,阻塞同步屬于一種悲觀的并發(fā)策略,認(rèn)為只要不去做正確的同步措施队寇,就肯定會出問題膘掰,無論共享的數(shù)據(jù)是否會出現(xiàn)競爭。隨著硬件指令的發(fā)展佳遣,有了另外一個(gè)選擇识埋,基于沖突檢測的樂觀并發(fā)策略凡伊,通俗的講就是先進(jìn)性操作,如果沒有其他線程爭用共享數(shù)據(jù)窒舟,那操作就成功了系忙,如果共享數(shù)據(jù)有爭用,產(chǎn)生了沖突辜纲,那就再進(jìn)行其他的補(bǔ)償措施(最常見的措施就是不斷的重試笨觅,直到成功為止),這種策略不需要把線程掛起耕腾,所以這種同步也被稱為非阻塞同步。
C杀糯、無同步方案扫俺,簡單的理解就是沒有共享變量需要不同的線程去爭用,目前有兩種方案固翰,一個(gè)是“可重入代碼”狼纬,這種代碼可以在執(zhí)行的任何時(shí)刻中斷它,轉(zhuǎn)而去執(zhí)行其他的另外一段代碼骂际,當(dāng)控制權(quán)返回時(shí)疗琉,程序繼續(xù)執(zhí)行,不會出現(xiàn)任何錯(cuò)誤歉铝。一個(gè)是“線程本地存儲”盈简,如果變量要被多線程訪問,可以使用volatile關(guān)鍵字來聲明它為“易變的“太示,以此來實(shí)現(xiàn)多線程之間的可見性柠贤。同時(shí)也可以通過ThreadLocal來實(shí)現(xiàn)線程本地存儲的功能,一個(gè)線程的Thread對象中都有一個(gè)ThreadLocalMap對象类缤,來實(shí)現(xiàn)KV數(shù)據(jù)的存儲臼勉。
4)主內(nèi)存和工作內(nèi)存?
Java內(nèi)存模型中規(guī)定了所有變量都存儲在主內(nèi)存中餐弱,每個(gè)線程還有自己的工作內(nèi)存泉懦,線程的工作內(nèi)存中保存了該線程使用到的變量的主內(nèi)存副本拷貝,線程對于變量的所有操作(讀取和賦值等)都必須在工作內(nèi)存中進(jìn)行性湿,而不能直接讀寫主內(nèi)存中的變量兼呵,不同線程之間無法直接訪問對方工作內(nèi)存中的變量,線程間值的傳遞均需要通過主內(nèi)存來完成降允。這里的主內(nèi)存和工作內(nèi)存恩闻,和java中堆的模型不是一個(gè)層次,主內(nèi)存主要對應(yīng)java堆中對象的實(shí)例數(shù)據(jù)部分剧董。
5)什么是自旋鎖幢尚?
自旋鎖在JDK1.6之后就默認(rèn)開啟了破停。基于之前的觀察尉剩,共享數(shù)據(jù)的鎖定狀態(tài)只會持續(xù)很短的時(shí)間真慢,為了這一小段時(shí)間而去掛起和恢復(fù)線程有點(diǎn)浪費(fèi),所以這里就做了一個(gè)處理理茎,讓后面請求鎖的那個(gè)線程在稍等一會黑界,但是不放棄處理器的執(zhí)行時(shí)間,看看持有鎖的線程能否快速釋放皂林。為了讓線程等待朗鸠,所以需要讓線程執(zhí)行一個(gè)忙循環(huán)也就是自旋操作。
在jdk6之后础倍,引入了自適應(yīng)的自旋鎖烛占,也就是等待的時(shí)間不再固定了,而是由上一次在同一個(gè)鎖上的自旋時(shí)間及鎖的擁有者狀態(tài)來決定沟启。
6)什么是鎖消除忆家?
虛擬機(jī)即時(shí)編譯器在運(yùn)行時(shí),對于代碼上要求同步德迹,但是被檢測到不可能存在共享數(shù)據(jù)競爭的鎖進(jìn)行消除芽卿。如果判斷一段代碼,在椎上的所有數(shù)據(jù)都不會逃逸出去被其他線程訪問到胳搞,那么認(rèn)為他是線程私有的卸例,同步加鎖也就沒有必要做了。
7)什么是鎖粗化流酬?
原則上币厕,我們在編寫代碼的時(shí)候,總是推薦將同步塊的作用范圍限制的盡量小芽腾,僅僅在共享數(shù)據(jù)的實(shí)際作用域才進(jìn)行同步旦装,這樣是為了使得需要同步的操作盡可能變小,如果存在鎖競爭摊滔,那等待鎖的線程也能盡快的拿到鎖阴绢。大部分情況下,這兒原則是正確的艰躺,但是如果一系列的連續(xù)操作都對同一個(gè)對象反復(fù)加鎖和解鎖呻袭,甚至鎖出現(xiàn)在循環(huán)體內(nèi),即使沒有線程競爭腺兴,頻繁的進(jìn)行互斥操作也會導(dǎo)致不必要的性能損耗左电。
8)什么是偏向鎖?
偏向鎖就是偏心的偏,意思是這個(gè)鎖會偏向第一個(gè)獲得他的線程篓足,如果接下來的執(zhí)行過程中段誊,該鎖沒有被其他線程獲取,則持有偏向鎖的線程將永遠(yuǎn)不需要再進(jìn)行同步栈拖。偏向鎖可以提高帶有同步但無競爭的程序性能连舍,也就是說他并不一定總是對程序運(yùn)行有利,如果程序中大多數(shù)的鎖都是被多個(gè)不同的線程訪問涩哟,那偏向模式就是多余的索赏,在具體問題具體分析的前提下,可以考慮是否使用偏向鎖贴彼。
9)關(guān)于輕量級鎖潜腻?
為了減少獲得鎖和釋放鎖所帶來的性能消耗,引入了“偏向鎖”和“輕量級鎖”锻弓,所以在Java SE1.6里鎖一共有四種狀態(tài)砾赔,無鎖狀態(tài),偏向鎖狀態(tài)青灼,輕量級鎖狀態(tài)和重量級鎖狀態(tài),它會隨著競爭情況逐漸升級妓盲。鎖可以升級但不能降級杂拨,意味著偏向鎖升級成輕量級鎖后不能降級成偏向鎖。這種鎖升級卻不能降級的策略悯衬,目的是為了提高獲得鎖和釋放鎖的效率弹沽。
10)什么場景下適合volatile?
volatile能夠?qū)崿F(xiàn)可見性筋粗,但是無法保證原子性策橘。可見性指一個(gè)線程修改了這個(gè)變量的指娜亿,新值對于其他線程來說是可以立即得知的丽已。而普通變量是不能做到這一點(diǎn)的,變量值在線程間傳遞均需要通過主內(nèi)存完成买决。volatile的變量在各個(gè)線程的工作內(nèi)存中不存在一致性問題(各個(gè)線程的工作內(nèi)存中volatile變量也可以存在不一致的情況沛婴,但是由于每次使用之前都要先刷新,執(zhí)行引擎看不到不一致的情況督赤,因此可以認(rèn)為不存在一致性問題)但是java里面的運(yùn)算并非原子操作的嘁灯,導(dǎo)致volatile變量運(yùn)算在并發(fā)下一樣是不安全的。
11)什么是CAS躲舌?
CAS 操作包含三個(gè)操作數(shù) —— 內(nèi)存位置(V)丑婿、預(yù)期原值(A)和新值(B)。 如果內(nèi)存位置的值與預(yù)期原值相匹配,那么處理器會自動(dòng)將該位置值更新為新值羹奉。否則秒旋,處理器不做任何操作。無論哪種情況尘奏,它都會在 CAS 指令之前返回該 位置的值滩褥。Java中通過Unsafe來實(shí)現(xiàn)了CAS。
12)如何實(shí)現(xiàn)互斥同步炫加?
java中最基本的互斥就是synchronized關(guān)鍵字瑰煎,synchronized在經(jīng)過編譯后,會在同步塊的前后分別形成monitorenter和moitorexit這兩個(gè)字節(jié)碼指令俗孝。在執(zhí)行monitorenter指令時(shí)酒甸,首先要去嘗試獲取對象的鎖,如果這個(gè)對象沒有被鎖定赋铝,或者當(dāng)前線程已經(jīng)擁有了那個(gè)對象的鎖插勤,把鎖的計(jì)數(shù)器加1,相應(yīng)的革骨,在執(zhí)行monitorexit指令時(shí)會把鎖計(jì)數(shù)器減1农尖,當(dāng)計(jì)數(shù)器為0時(shí),鎖就被釋放了良哲。如果獲取對象的鎖失敗盛卡,當(dāng)當(dāng)前線程就要阻塞等待,直到對象的鎖被另一個(gè)線程釋放為止筑凫。synchronized對于同一個(gè)線程來說是可重入的滑沧,不會出現(xiàn)自己把自己鎖死的問題。除了synchronized指望巍实,JUC中的Lock也能實(shí)現(xiàn)互斥同步滓技,ReentrantLock,寫法上更加可見棚潦,lock和unlock配合try/finally來配合完成令漂,ReentrantLock比synchronized有幾個(gè)高級的特性。
13)ReentrantLock的高級特性有那幾個(gè)瓦盛?
1洗显、等待可中斷,當(dāng)持有鎖的線程長期不釋放的時(shí)候原环,正在等待的線程可以選擇放棄等待挠唆,改為處理其他事情;
2嘱吗、可以實(shí)現(xiàn)公平鎖玄组,公平鎖指多個(gè)線程在等待同一個(gè)鎖時(shí)滔驾,必須按照申請鎖的順序依次獲得鎖,synchronized是非公平鎖俄讹,ReentrantLock默認(rèn)也是非公平的哆致,只不過可以通過構(gòu)造函數(shù)來制定實(shí)現(xiàn)公平鎖;
3患膛、鎖綁定多個(gè)條件摊阀,ReentrantLock對象可以同時(shí)綁定多個(gè)Condition對象,在synchronized中踪蹬,鎖對象的wait/notify/notifyall方法可以實(shí)現(xiàn)一個(gè)隱含的條件胞此,如果要多一個(gè)條件關(guān)聯(lián)的時(shí)候,就需要額外的增加一個(gè)鎖跃捣;
14)關(guān)于鎖的幾個(gè)使用建議漱牵?
1、使用并發(fā)包中的類疚漆,并發(fā)包中的類大多數(shù)采用了lock-free等算法酣胀,減少了多線程情況下的資源的鎖競爭,因此對于線程間的共享操作的資源而言娶聘,應(yīng)盡量使用并發(fā)包中的類來實(shí)現(xiàn)闻镶;
2、盡可能少用鎖丸升,沒必要用鎖的地方就不要用了儒溉;
3、拆分鎖发钝,即把獨(dú)占鎖拆分為多把鎖(這個(gè)不一定完全適用);
4波闹、去除讀寫操作的互斥鎖酝豪,在修改時(shí)加鎖,并復(fù)制對象進(jìn)行修改精堕,修改完畢之后切換對象的引用孵淘,而讀取是則不加鎖,這種方式成為CopyOnWrite歹篓,CopyOnWriteArrayList就是COW的典型實(shí)現(xiàn)瘫证,可以明顯提升讀的性能;
15)關(guān)于synchronized的幾個(gè)注意點(diǎn)庄撮?
1背捌、當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí), 另一個(gè)線程仍然可以訪問該object中的非synchronized(this)同步代碼塊洞斯;
2毡庆、當(dāng)兩個(gè)并發(fā)線程訪問同一個(gè)對象object中的這個(gè)synchronized(this)同步代碼塊時(shí), 一個(gè)時(shí)間內(nèi)只能有一個(gè)線程得到執(zhí)行。另一個(gè)線程必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊么抗;
3毅否、尤其關(guān)鍵的是,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí)蝇刀, 其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞螟加;
4、Java中的每一個(gè)對象都可以作為鎖吞琐,對于同步方法捆探,鎖是當(dāng)前實(shí)例對象,對于靜態(tài)同步方法顽分,鎖是當(dāng)前對象的Class對象徐许,對于同步方法塊,鎖是Synchonized括號里配置的對象卒蘸;
4雌隅、Hashset為什么不加重復(fù)的值 http://blog.sina.com.cn/s/blog_69fca0a20100knsh.html
- HashSet中不允許有重復(fù)的元素。在向HashSet中添加(add())元素的時(shí)候缸沃,對于重復(fù)的元素恰起,只在HashSet中保留一個(gè)副本。
- HashSet中元素的順序是隨機(jī)的趾牧,包括添加(add())和輸出都是無序的检盼。
使用迭代器輸出HashSet中的元素,例如:
Iterator it = hashSet.iterator();
while(it.hasNext()){
System.out.println((String)it.next());
}
假設(shè)有一個(gè)Person類:
class Person{
private String name;
private Integer age;
getter翘单、setter方法
}
構(gòu)造兩個(gè)Person的實(shí)例:
Person p1 = new Person();
p1.setName("shirdrn");
p1.setAge(new Integer(26));
Person p2 = new Person();
p2.setName("shirdrn");
p2.setAge(new Integer(26));
加入到HashSet中:
Set hashSet = new HashSet();
hashSet.add(p1);
hashSet.add(p2);
此時(shí)的hashSet.size()=2吨枉。這主要是由于Object擁有hashCode()和equals()兩個(gè)方法,它認(rèn)為具有相同的hashcode的對象才是同一個(gè)對象哄芜,即對同一個(gè)對象的引用貌亭。
所以必須在對應(yīng)的實(shí)體類中重寫hashCode()和equals()兩個(gè)方法。在Person類中重寫hashCode()和equals()兩個(gè)方法认臊,如下所示:
public boolean equals(Object o){
if(this == o){
return true;
}
if(! (o instanceof Person)){
return false;
}
final Person other = (Person)o;
if(this.name.equals(other.getName()) && this.age.equals(other.getAge())){
return true;
}
else{
return false;
}
}
public int hashCode(){
int result;
result = (name == null?0:name.hashCode());
result=37*result+(age==null?0:age.hashCode());
return result;
}
這時(shí)圃庭,再進(jìn)行上面的測試,發(fā)現(xiàn)hashSet.size()=1失晴。此時(shí)剧腻,對象p1和p2具有相同的hashcode,HashSet認(rèn)為添加的兩個(gè)Person實(shí)例是同一個(gè)對象涂屁,只把一個(gè)添加到集合里面书在。
5、hashmap實(shí)現(xiàn)原理淺析 http://www.cnblogs.com/lzrabbit/p/3721067.html
1)HashMap和Hashtable的區(qū)別
·兩者最主要的區(qū)別在于Hashtable是線程安全胯陋,而HashMap則非線程安全
Hashtable的實(shí)現(xiàn)方法里面都添加了synchronized關(guān)鍵字來確保線程同步蕊温,因此相對而言HashMap性能會高一些袱箱,我們平時(shí)使用時(shí)若無特殊需求建議使用HashMap,在多線程環(huán)境下若使用HashMap需要使用Collections.synchronizedMap()方法來獲取一個(gè)線程安全的集合
·HashMap可以使用null作為key义矛,而Hashtable則不允許null作為key
HashMap以null作為key時(shí)发笔,總是存儲在table數(shù)組的第一個(gè)節(jié)點(diǎn)上
·HashMap是對Map接口的實(shí)現(xiàn),HashTable實(shí)現(xiàn)了Map接口和Dictionary抽象類
·HashMap的初始容量為16凉翻,Hashtable初始容量為11了讨,兩者的填充因子默認(rèn)都是0.75
·HashMap擴(kuò)容時(shí)是當(dāng)前容量翻倍即:capacity2,Hashtable擴(kuò)容時(shí)是容量翻倍+1即:capacity2+1
·兩者計(jì)算hash的方法不同
·HashMap和Hashtable的底層實(shí)現(xiàn)都是數(shù)組+鏈表結(jié)構(gòu)實(shí)現(xiàn)
2)HashSet和HashMap制轰、Hashtable的區(qū)別
HashSet不是key value結(jié)構(gòu)前计,僅僅是存儲不重復(fù)的元素,相當(dāng)于簡化版的HashMap垃杖,只是包含HashMap中的key而已男杈。HashSet里面的HashMap所有的value都是同一個(gè)Object而已,因此HashSet也是非線程安全的调俘,至于HashSet和Hashtable的區(qū)別伶棒,HashSet就是個(gè)簡化的HashMap的
3)HashMap的創(chuàng)建
HashMap默認(rèn)初始化時(shí)會創(chuàng)建一個(gè)默認(rèn)容量為16的Entry數(shù)組,默認(rèn)加載因子為0.75彩库,同時(shí)設(shè)置臨界值為16*0.75
6肤无、如何理解 Java反射機(jī)制 http://blog.csdn.net/nemo2011/article/details/6585683
·Java反射機(jī)制
JAVA反射機(jī)制是在運(yùn)行狀態(tài)中,對于任意一個(gè)類骇钦,都能夠知道這個(gè)類的所有屬性和方法宛渐;對于任意一個(gè)對象,都能夠調(diào)用它的任意一個(gè)方法眯搭;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對象的方法的功能稱為java語言的反射機(jī)制窥翩。
·Java反射機(jī)制主要提供了以下功能:
·在運(yùn)行時(shí)判斷任意一個(gè)對象所屬的類;
·在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對象鳞仙;
·在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法鳍烁;
·在運(yùn)行時(shí)調(diào)用任意一個(gè)對象的方法;
·生成動(dòng)態(tài)代理繁扎。
·獲取類的方法:
Class.forName(“類的路徑”);
類名.class
實(shí)例.getClass()
7、什么是動(dòng)態(tài)代理 http://huangnx.com/2016/10/17/proxyDesignDesc/
靜態(tài)代理: 在程序運(yùn)行前就已經(jīng)存在代理類的字節(jié)碼文件糊闽。代理類和委托類的關(guān)系在運(yùn)行前就確定了
動(dòng)態(tài)代理: 動(dòng)態(tài)代理類的源碼是在程序運(yùn)行期由JVM根據(jù)反射機(jī)制動(dòng)態(tài)生成的梳玫。代理類和委托類的關(guān)系是在程序運(yùn)行時(shí)確定的
8、JavaCAS原理深度分析 http://blog.csdn.net/hsuxu/article/details/9467651
CAS:Compare and Swap, 翻譯成比較并交換右犹。
java.util.concurrent包中借助CAS實(shí)現(xiàn)了區(qū)別于synchronouse同步鎖的一種樂觀鎖提澎。
·CAS有3個(gè)操作數(shù),內(nèi)存值V念链,舊的預(yù)期值A(chǔ)盼忌,要修改的新值B积糯。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為B谦纱,否則什么都不做看成。
·非阻塞算法 (nonblocking algorithms)
一個(gè)線程的失敗或者掛起不應(yīng)該影響其他線程的失敗或掛起的算法。
現(xiàn)代的CPU提供了特殊的指令跨嘉,可以自動(dòng)更新共享數(shù)據(jù)川慌,而且能夠檢測到其他線程的干擾,而 compareAndSet() 就用這些代替了鎖定祠乃。
拿出AtomicInteger來研究在沒有鎖的情況下是如何做到數(shù)據(jù)正確性的梦重。
private volatile int value;
首先毫無以為,在沒有鎖的機(jī)制下可能需要借助volatile原語亮瓷,保證線程間的數(shù)據(jù)是可見的(共享的)琴拧。這樣才獲取變量的值的時(shí)候才能直接讀取。
public final int get() {
return value;
}
然后來看看++i是怎么做到的嘱支。
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
在這里采用了CAS操作蚓胸,每次從內(nèi)存中讀取數(shù)據(jù)然后將此數(shù)據(jù)和+1后的結(jié)果進(jìn)行CAS操作,如果成功就返回結(jié)果斗塘,否則重試直到成功為止赢织。
而compareAndSet利用JNI來完成CPU指令的操作。
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
整體的過程就是這樣子的馍盟,利用CPU的CAS指令于置,同時(shí)借助JNI來完成Java的非阻塞算法。其它原子操作都是利用類似的特性完成的贞岭。
其中unsafe.compareAndSwapInt(this, valueOffset, expect, update);類似:
if (this == expect) { this = update return true;} else {return false;}
·CAS原理
CAS通過調(diào)用JNI的代碼實(shí)現(xiàn)的八毯。JNI:java Native Interface為JAVA本地調(diào)用,允許java調(diào)用其他語言瞄桨。
而compareAndSwapInt就是借助C來調(diào)用CPU底層指令實(shí)現(xiàn)的话速。 下面是sun.misc.Unsafe類的compareAndSwapInt()方法的源代碼:
public final native boolean compareAndSwapInt(Object o, long offset,
int expected,
int x);
程序會根據(jù)當(dāng)前處理器的類型來決定是否為cmpxchg指令添加lock前綴。如果程序是在多處理器上運(yùn)行芯侥,就為cmpxchg指令加上lock前綴(lock cmpxchg)泊交。反之,如果程序是在單處理器上運(yùn)行柱查,就省略lock前綴(單處理器自身會維護(hù)單處理器內(nèi)的順序一致性廓俭,不需要lock前綴提供的內(nèi)存屏障效果)。
intel的手冊對lock前綴的說明如下:
確保對內(nèi)存的讀-改-寫操作原子執(zhí)行唉工;禁止該指令與之前和之后的讀和寫指令重排序研乒;把寫緩沖區(qū)中的所有數(shù)據(jù)刷新到內(nèi)存中。
備注知識:
關(guān)于CPU的鎖有如下3種:
3.1 處理器自動(dòng)保證基本內(nèi)存操作的原子性
首先處理器會自動(dòng)保證基本的內(nèi)存操作的原子性淋硝。處理器保證從系統(tǒng)內(nèi)存當(dāng)中讀取或者寫入一個(gè)字節(jié)是原子的雹熬,意思是當(dāng)一個(gè)處理器讀取一個(gè)字節(jié)時(shí)宽菜,其他處理器不能訪問這個(gè)字節(jié)的內(nèi)存地址。奔騰6和最新的處理器能自動(dòng)保證單處理器對同一個(gè)緩存行里進(jìn)行16/32/64位的操作是原子的竿报,但是復(fù)雜的內(nèi)存操作處理器不能自動(dòng)保證其原子性铅乡,比如跨總線寬度,跨多個(gè)緩存行仰楚,跨頁表的訪問隆判。但是處理器提供總線鎖定和緩存鎖定兩個(gè)機(jī)制來保證復(fù)雜內(nèi)存操作的原子性。
3.2 使用總線鎖保證原子性
第一個(gè)機(jī)制是通過總線鎖保證原子性僧界。如果多個(gè)處理器同時(shí)對共享變量進(jìn)行讀改寫(i++就是經(jīng)典的讀改寫操作)操作侨嘀,那么共享變量就會被多個(gè)處理器同時(shí)進(jìn)行操作,這樣讀改寫操作就不是原子的捂襟,操作完之后共享變量的值會和期望的不一致咬腕,舉個(gè)例子:如果i=1,我們進(jìn)行兩次i++操作,我們期望的結(jié)果是3葬荷,但是有可能結(jié)果是2涨共。
原因是有可能多個(gè)處理器同時(shí)從各自的緩存中讀取變量i,分別進(jìn)行加一操作宠漩,然后分別寫入系統(tǒng)內(nèi)存當(dāng)中举反。那么想要保證讀改寫共享變量的操作是原子的,就必須保證CPU1讀改寫共享變量的時(shí)候扒吁,CPU2不能操作緩存了該共享變量內(nèi)存地址的緩存火鼻。
處理器使用總線鎖就是來解決這個(gè)問題的。所謂總線鎖就是使用處理器提供的一個(gè)LOCK#信號雕崩,當(dāng)一個(gè)處理器在總線上輸出此信號時(shí)魁索,其他處理器的請求將被阻塞住,那么該處理器可以獨(dú)占使用共享內(nèi)存。
3.3 使用緩存鎖保證原子性
第二個(gè)機(jī)制是通過緩存鎖定保證原子性盼铁。在同一時(shí)刻我們只需保證對某個(gè)內(nèi)存地址的操作是原子性即可粗蔚,但總線鎖定把CPU和內(nèi)存之間通信鎖住了,這使得鎖定期間饶火,其他處理器不能操作其他內(nèi)存地址的數(shù)據(jù)鹏控,所以總線鎖定的開銷比較大,最近的處理器在某些場合下使用緩存鎖定代替總線鎖定來進(jìn)行優(yōu)化肤寝。
頻繁使用的內(nèi)存會緩存在處理器的L1牧挣,L2和L3高速緩存里,那么原子操作就可以直接在處理器內(nèi)部緩存中進(jìn)行醒陆,并不需要聲明總線鎖,在奔騰6和最近的處理器中可以使用“緩存鎖定”的方式來實(shí)現(xiàn)復(fù)雜的原子性裆针。所謂“緩存鎖定”就是如果緩存在處理器緩存行中內(nèi)存區(qū)域在LOCK操作期間被鎖定刨摩,當(dāng)它執(zhí)行鎖操作回寫內(nèi)存時(shí)寺晌,處理器不在總線上聲言LOCK#信號,而是修改內(nèi)部的內(nèi)存地址澡刹,并允許它的緩存一致性機(jī)制來保證操作的原子性,因?yàn)榫彺嬉恢滦詸C(jī)制會阻止同時(shí)修改被兩個(gè)以上處理器緩存的內(nèi)存區(qū)域數(shù)據(jù),當(dāng)其他處理器回寫已被鎖定的緩存行的數(shù)據(jù)時(shí)會起緩存行無效扯罐,在例1中扛门,當(dāng)CPU1修改緩存行中的i時(shí)使用緩存鎖定,那么CPU2就不能同時(shí)緩存了i的緩存行嚷闭。
但是有兩種情況下處理器不會使用緩存鎖定攒岛。第一種情況是:當(dāng)操作的數(shù)據(jù)不能被緩存在處理器內(nèi)部,或操作的數(shù)據(jù)跨多個(gè)緩存行(cache line)胞锰,則處理器會調(diào)用總線鎖定灾锯。第二種情況是:有些處理器不支持緩存鎖定。對于Inter486和奔騰處理器,就算鎖定的內(nèi)存區(qū)域在處理器的緩存行中也會調(diào)用總線鎖定嗅榕。
以上兩個(gè)機(jī)制我們可以通過Inter處理器提供了很多LOCK前綴的指令來實(shí)現(xiàn)顺饮。比如位測試和修改指令BTS,BTR凌那,BTC兼雄,交換指令XADD,CMPXCHG和其他一些操作數(shù)和邏輯指令帽蝶,比如ADD(加)赦肋,OR(或)等,被這些指令操作的內(nèi)存區(qū)域就會加鎖嘲碱,導(dǎo)致其他處理器不能同時(shí)訪問它金砍。
·CAS缺點(diǎn)
CAS雖然很高效的解決原子操作,但是CAS仍然存在三大問題麦锯。ABA問題恕稠,循環(huán)時(shí)間長開銷大和只能保證一個(gè)共享變量的原子操作
- ABA問題。因?yàn)镃AS需要在操作值的時(shí)候檢查下值有沒有發(fā)生變化扶欣,如果沒有發(fā)生變化則更新鹅巍,但是如果一個(gè)值原來是A,變成了B料祠,又變成了A骆捧,那么使用CAS進(jìn)行檢查時(shí)會發(fā)現(xiàn)它的值沒有發(fā)生變化,但是實(shí)際上卻變化了髓绽。ABA問題的解決思路就是使用版本號敛苇。在變量前面追加上版本號,每次變量更新的時(shí)候把版本號加一顺呕,那么A-B-A 就會變成1A-2B-3A枫攀。
從Java1.5開始JDK的atomic包里提供了一個(gè)類AtomicStampedReference來解決ABA問題括饶。這個(gè)類的compareAndSet方法作用是首先檢查當(dāng)前引用是否等于預(yù)期引用,并且當(dāng)前標(biāo)志是否等于預(yù)期標(biāo)志来涨,如果全部相等图焰,則以原子方式將該引用和該標(biāo)志的值設(shè)置為給定的更新值。 - 循環(huán)時(shí)間長開銷大蹦掐。自旋CAS如果長時(shí)間不成功技羔,會給CPU帶來非常大的執(zhí)行開銷。如果JVM能支持處理器提供的pause指令那么效率會有一定的提升卧抗,pause指令有兩個(gè)作用藤滥,第一它可以延遲流水線執(zhí)行指令(de-pipeline),使CPU不會消耗過多的執(zhí)行資源,延遲的時(shí)間取決于具體實(shí)現(xiàn)的版本颗味,在一些處理器上延遲時(shí)間是零超陆。第二它可以避免在退出循環(huán)的時(shí)候因內(nèi)存順序沖突(memory order violation)而引起CPU流水線被清空(CPU pipeline flush),從而提高CPU的執(zhí)行效率浦马。
- 只能保證一個(gè)共享變量的原子操作时呀。當(dāng)對一個(gè)共享變量執(zhí)行操作時(shí),我們可以使用循環(huán)CAS的方式來保證原子操作晶默,但是對多個(gè)共享變量操作時(shí)谨娜,循環(huán)CAS就無法保證操作的原子性,這個(gè)時(shí)候就可以用鎖磺陡,或者有一個(gè)取巧的辦法趴梢,就是把多個(gè)共享變量合并成一個(gè)共享變量來操作。比如有兩個(gè)共享變量i=2,j=a币他,合并一下ij=2a坞靶,然后用CAS來操作ij。從Java1.5開始JDK提供了AtomicReference類來保證引用對象之間的原子性蝴悉,你可以把多個(gè)變量放在一個(gè)對象里來進(jìn)行CAS操作彰阴。
·concurrent包的實(shí)現(xiàn)
由于java的CAS同時(shí)具有 volatile 讀和volatile寫的內(nèi)存語義,因此Java線程之間的通信現(xiàn)在有了下面四種方式:
A線程寫volatile變量拍冠,隨后B線程讀這個(gè)volatile變量尿这。
A線程寫volatile變量,隨后B線程用CAS更新這個(gè)volatile變量庆杜。
A線程用CAS更新一個(gè)volatile變量射众,隨后B線程用CAS更新這個(gè)volatile變量。
A線程用CAS更新一個(gè)volatile變量晃财,隨后B線程讀這個(gè)volatile變量叨橱。
Java的CAS會使用現(xiàn)代處理器上提供的高效機(jī)器級別原子指令,這些原子指令以原子方式對內(nèi)存執(zhí)行讀-改-寫操作,這是在多處理器中實(shí)現(xiàn)同步的關(guān)鍵(從本質(zhì)上來說罗洗,能夠支持原子性讀-改-寫指令的計(jì)算機(jī)器嘉裤,是順序計(jì)算圖靈機(jī)的異步等價(jià)機(jī)器,因此任何現(xiàn)代的多處理器都會去支持某種能對內(nèi)存執(zhí)行原子性讀-改-寫操作的原子指令)栖博。同時(shí),volatile變量的讀/寫和CAS可以實(shí)現(xiàn)線程之間的通信厢洞。把這些特性整合在一起仇让,就形成了整個(gè)concurrent包得以實(shí)現(xiàn)的基石。如果我們仔細(xì)分析concurrent包的源代碼實(shí)現(xiàn)躺翻,會發(fā)現(xiàn)一個(gè)通用化的實(shí)現(xiàn)模式:
首先丧叽,聲明共享變量為volatile;
然后公你,使用CAS的原子條件更新來實(shí)現(xiàn)線程之間的同步踊淳;
同時(shí),配合以volatile的讀/寫和CAS所具有的volatile讀和寫的內(nèi)存語義來實(shí)現(xiàn)線程之間的通信陕靠。
AQS迂尝,非阻塞數(shù)據(jù)結(jié)構(gòu)和原子變量類(java.util.concurrent.atomic包中的類),這些concurrent包中的基礎(chǔ)類都是使用這種模式來實(shí)現(xiàn)的剪芥,而concurrent包中的高層類又是依賴于這些基礎(chǔ)類來實(shí)現(xiàn)的垄开。從整體來看,concurrent包的實(shí)現(xiàn)示意圖如下:
9税肪、Java類加載機(jī)制 http://blog.csdn.net/love_Javc_you/article/details/38081683
·簡單過程
Java程序運(yùn)行的場所是內(nèi)存溉躲,當(dāng)在命令行下執(zhí)行:
java HelloWorld命令的時(shí)候,JVM會將HelloWorld.class加載到內(nèi)存中益兄,并形成一個(gè)Class的對象HelloWorld.class锻梳。其中的過程就是類加載過程:
1、尋找jre目錄净捅,尋找jvm.dll疑枯,并初始化JVM;
2灸叼、產(chǎn)生一個(gè)Bootstrap Loader(啟動(dòng)類加載器)神汹;
3、Bootstrap Loader自動(dòng)加載Extended Loader(標(biāo)準(zhǔn)擴(kuò)展類加載器)古今,并將其父Loader設(shè)為Bootstrap Loader屁魏。
4、Bootstrap Loader自動(dòng)加載AppClass Loader(系統(tǒng)類加載器)捉腥,并將其父Loader設(shè)為Extended Loader氓拼。
5、最后由AppClass Loader加載HelloWorld類。
·類加載器的特點(diǎn)
1桃漾、運(yùn)行一個(gè)程序時(shí)坏匪,總是由AppClass Loader(系統(tǒng)類加載器)開始加載指定的類。
2撬统、在加載類時(shí)适滓,每個(gè)類加載器會將加載任務(wù)上交給其父,如果其父找不到恋追,再由自己去加載凭迹。
3、Bootstrap Loader(啟動(dòng)類加載器)是最頂級的類加載器了苦囱,其父加載器為null.
·類加載有三種方式:
1嗅绸、命令行啟動(dòng)應(yīng)用時(shí)候由JVM初始化加載
2、通過Class.forName()方法動(dòng)態(tài)加載
3撕彤、通過ClassLoader.loadClass()方法動(dòng)態(tài)加載
//使用ClassLoader.loadClass()來加載類鱼鸠,不會執(zhí)行初始化塊
//使用Class.forName()來加載類,默認(rèn)會執(zhí)行初始化塊
//使用Class.forName()來加載類羹铅,并指定ClassLoader蚀狰,初始化時(shí)不執(zhí)行靜態(tài)塊
10、 JDK7與JDK8中HashMap的實(shí)現(xiàn) https://my.oschina.net/hosee/blog/618953
11睦裳、 Java中常用的鎖分析總結(jié) http://www.tuicool.com/articles/NnQjyq
ReentrantLock造锅、ReentrantReadWriteLock及Sychronized簡介
(a) 類繼承結(jié)構(gòu)
ReentrantLock類繼承結(jié)構(gòu):
ReentrantReadWriteLick類繼承結(jié)構(gòu):
簡述:通過類的繼承結(jié)構(gòu)可以看出ReentrantLock 和 ReentrantReadWriteLock是擁有者兩個(gè)不同類繼承結(jié)構(gòu)的體系,兩者并無關(guān)聯(lián)廉邑。
Ps:Sychronized是一個(gè)關(guān)鍵字
(b) 幾個(gè)相關(guān)概念
什么是可重入鎖 :可重入鎖的概念是自己可以再次獲取自己的內(nèi)部鎖哥蔚。舉個(gè)例子,比如一條線程獲得了某個(gè)對象的鎖蛛蒙,此時(shí)這個(gè)對象鎖還沒有釋放糙箍,當(dāng)其再次想要獲取這個(gè)對象的鎖的時(shí)候還是可以獲取的(如果不可重入的鎖的話,此刻會造成死鎖)牵祟。說的更高深一點(diǎn)可重入鎖是一種遞歸無阻塞的同步機(jī)制深夯。
什么叫讀寫鎖 :讀寫鎖拆成讀鎖和寫鎖來理解。讀鎖可以共享诺苹,多個(gè)線程可以同時(shí)擁有讀鎖咕晋,但是寫鎖卻只能只有一個(gè)線程擁有,而且獲取寫鎖的時(shí)候其他線程都已經(jīng)釋放了讀鎖收奔,而且該線程獲取寫鎖之后掌呜,其他線程不能再獲取讀鎖。簡單的說就是寫鎖是排他鎖坪哄,讀鎖是共享鎖质蕉。
獲取鎖涉及到的兩個(gè)概念即 公平和非公平 :公平表示線程獲取鎖的順序是按照線程加鎖的順序來分配的势篡,即先來先得的FIFO順序。而非公平就是一種獲取鎖的搶占機(jī)制模暗,和公平相對就是先來不一定先得禁悠,這個(gè)方式可能造成某些線程饑餓(一直拿不到鎖)。
(c) ReentrantLock兑宇,ReentrantReadWriteLock碍侦,Sychronized用法即作用
ReentrantLock : 類ReentrantLock實(shí)現(xiàn)了Lock,它擁有與Sychronized相同的并發(fā)性和內(nèi)存語義隶糕,但是添加了類似鎖投票祝钢、定時(shí)鎖等候和可中斷等候的一些特性。此外若厚,它還提供了在與激烈爭用情況下更佳的性能(說白了就是ReentrantLock和Sychronized差不多,線程間都是完全互斥的蜒什,一個(gè)時(shí)刻只能有一個(gè)線程獲取到鎖测秸,執(zhí)行被鎖住的代碼,但ReentrantLock相對于Sychronized提供了更加豐富的功能并且在線程調(diào)度上做了優(yōu)化灾常,JVM調(diào)度使用ReentrantLock的線程會更快)
ReentrantReadWriteLock: 類ReentrantReadWriteLock實(shí)現(xiàn)了ReadWirteLock接口霎冯。它和ReentrantLock是不同的兩套實(shí)現(xiàn),在類繼承結(jié)構(gòu)上并無關(guān)聯(lián)钞瀑。和ReentrantLock定義的互斥鎖不同的是沈撞,ReentrantReadWriteLock定義了兩把鎖即讀鎖和寫鎖。讀鎖可以共享雕什,即同一個(gè)資源可以讓多個(gè)線程獲取讀鎖缠俺。這個(gè)和ReentrantLock(或者sychronized)相比大大提高了讀的性能。在需要對資源進(jìn)行寫入的時(shí)候在會加寫鎖達(dá)到互斥的目的
12贷岸、volatile與synchronized的區(qū)別 http://blog.csdn.net/fanaticism1/article/details/9966163
1)鎖提供了兩種主要特性:互斥(mutual exclusion) 和可見性(visibility)壹士。互斥即一次只允許一個(gè)線程持有某個(gè)特定的鎖偿警,因此可使用該特性實(shí)現(xiàn)對共享數(shù)據(jù)的協(xié)調(diào)訪問協(xié)議躏救,這樣,一次就只有一個(gè)線程能夠使用該共享數(shù)據(jù)螟蒸『惺梗可見性必須確保釋放鎖之前對共享數(shù)據(jù)做出的更改對于隨后獲得該鎖的另一個(gè)線程是可見的 —— 如果沒有同步機(jī)制提供的這種可見性保證,線程看到的共享變量可能是修改前的值或不一致的值七嫌,這將引發(fā)許多嚴(yán)重問題少办。
2)在Java中,為了保證多線程讀寫數(shù)據(jù)時(shí)保證數(shù)據(jù)的一致性,可以采用兩種方式:
·同步:如用synchronized關(guān)鍵字,或者使用鎖對象.
·volatile:使用volatile關(guān)鍵字
用一句話概括volatile,它能夠使變量在值發(fā)生改變時(shí)能盡快地讓其他線程知道.
·volatile詳解
首先我們要先意識到有這樣的現(xiàn)象,編譯器為了加快程序運(yùn)行的速度,對一些變量的寫操作會先在寄存器或者是CPU緩存上進(jìn)行,最后才寫入內(nèi)存.而在這個(gè)過程,變量的新值對其他線程是不可見的.而volatile的作用就是使它修飾的變量的讀寫操作都必須在內(nèi)存中進(jìn)行!
·volatile與synchronized
volatile本質(zhì)是在告訴jvm當(dāng)前變量在寄存器中的值是不確定的,需要從主存中讀取,synchronized則是鎖定當(dāng)前變量,只有當(dāng)前線程可以訪問該變量,其他線程被阻塞住.
volatile僅能使用在變量級別,synchronized則可以使用在變量,方法.
volatile僅能實(shí)現(xiàn)變量的修改可見性,但不具備原子特性,而synchronized則可以保證變量的修改可見性和原子性.
volatile不會造成線程的阻塞,而synchronized可能會造成線程的阻塞.
volatile標(biāo)記的變量不會被編譯器優(yōu)化,而synchronized標(biāo)記的變量可以被編譯器優(yōu)化.
3)因此,在使用volatile關(guān)鍵字時(shí)要慎重抄瑟,并不是只要簡單類型變量使用volatile修飾凡泣,對這個(gè)變量的所有操作都是原來操作枉疼,當(dāng)變量的值由自身的上一個(gè)決定時(shí),如n=n+1鞋拟、n++ 等骂维,volatile關(guān)鍵字將失效,只有當(dāng)變量的值和自身上一個(gè)值無關(guān)時(shí)對該變量的操作才是原子級別的贺纲,如n = m + 1航闺,這個(gè)就是原級別的。所以在使用volatile關(guān)鍵時(shí)一定要謹(jǐn)慎猴誊,如果自己沒有把握潦刃,可以使用synchronized來代替volatile。
總結(jié):volatile本質(zhì)是在告訴JVM當(dāng)前變量在寄存器中的值是不確定的懈叹,需要從主存中讀取乖杠。可以實(shí)現(xiàn)synchronized的部分效果澄成,但當(dāng)n=n+1,n++等時(shí)胧洒,volatile關(guān)鍵字將失效,不能起到像synchronized一樣的線程同步的效果墨状。
13卫漫、 Java中的object類有哪些方法 :
hashcode() 、equal() 肾砂、wait() 列赎、notify() 、notifyall()镐确、 finalize() 等等
14包吝、 深復(fù)制與淺復(fù)制的區(qū)別
淺拷貝是指在拷貝對象時(shí),對于基本數(shù)據(jù)類型的變量會重新復(fù)制一份源葫,而對于引用類型的變量只是對引用進(jìn)行拷貝漏策,有對引用指向的對象進(jìn)行拷貝。
而深拷貝是指在拷貝對象時(shí)臼氨,同時(shí)會對引用指向的對象進(jìn)行拷貝掺喻。
區(qū)別就在于是否對 對象中的引用變量所指向的對象進(jìn)行拷貝。
15储矩、 深入剖析Java中的裝箱和拆箱 http://www.cnblogs.com/dolphin0520/p/3780005.html
在Java SE5之前感耙,如果要生成一個(gè)數(shù)值為10的Integer對象:Integer i = new Integer(10);
而在從Java SE5開始就提供了自動(dòng)裝箱的特性,如果要生成一個(gè)數(shù)值為10的Integer對象持隧,Integer i = 10;
這個(gè)過程中會自動(dòng)根據(jù)數(shù)值創(chuàng)建對應(yīng)的 Integer對象即硼,這就是裝箱。
拆箱 跟裝箱對應(yīng)屡拨,就是自動(dòng)將包裝器類型轉(zhuǎn)換為基本數(shù)據(jù)類型:
Integer i = 10; //裝箱
int n = i; //拆箱
裝箱就是 :自動(dòng)將基本數(shù)據(jù)類型轉(zhuǎn)換為包裝器類型只酥;
拆箱就是 :自動(dòng)將包裝器類型轉(zhuǎn)換為基本數(shù)據(jù)類型褥实。
在裝箱的時(shí)候自動(dòng)調(diào)用的是Integer的valueOf(int)方法。而在拆箱的時(shí)候自動(dòng)調(diào)用的是Integer的intValue方法裂允。
A损离、 在通過valueOf方法創(chuàng)建Integer對象的時(shí)候,如果數(shù)值在[-128,127]之間绝编,便返回指向IntegerCache.cache中已經(jīng)存在的對象的引用僻澎;否則創(chuàng)建一個(gè)新的Integer對象。
B十饥、談?wù)処nteger i = new Integer(xxx)和Integer i =xxx;這兩種方式的區(qū)別窟勃。
1)第一種方式不會觸發(fā)自動(dòng)裝箱的過程;而第二種方式會觸發(fā)逗堵;
2)在執(zhí)行效率和資源占用上的區(qū)別秉氧。第二種方式的執(zhí)行效率和資源占用在一般性情況下要優(yōu)于第一種情況(注意這并不是絕對的)。
C蜒秤、下面程序的輸出結(jié)果是什么谬运?
public class Main {
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
Long h = 2L;
System.out.println(c==d); //true
System.out.println(e==f); //false
System.out.println(c==(a+b)); //true
System.out.println(c.equals(a+b));//true
System.out.println(g==(a+b)); //true
System.out.println(g.equals(a+b));//false
System.out.println(g.equals(a+h));//true
}
}
先別看輸出結(jié)果,讀者自己想一下這段代碼的輸出結(jié)果是什么垦藏。這里面需要注意的是:當(dāng) "=="運(yùn)算符的兩個(gè)操作數(shù)都是 包裝器類型的引用,則是比較指向的是否是同一個(gè)對象伞访,而如果其中有一個(gè)操作數(shù)是表達(dá)式(即包含算術(shù)運(yùn)算)則比較的是數(shù)值(即會觸發(fā)自動(dòng)拆箱的過程)掂骏。另外,對于包裝器類型厚掷,equals方法并不會進(jìn)行類型轉(zhuǎn)換弟灼。
第一個(gè)和第二個(gè)輸出結(jié)果沒有什么疑問。第三句由于 a+b包含了算術(shù)運(yùn)算冒黑,因此會觸發(fā)自動(dòng)拆箱過程(會調(diào)用intValue方法)田绑,因此它們比較的是數(shù)值是否相等。而對于c.equals(a+b)會先觸發(fā)自動(dòng)拆箱過程抡爹,再觸發(fā)自動(dòng)裝箱過程掩驱,也就是說a+b,會先各自調(diào)用intValue方法冬竟,得到了加法運(yùn)算后的數(shù)值之后欧穴,便調(diào)用Integer.valueOf方法,再進(jìn)行equals比較泵殴。同理對于后面的也是這樣涮帘,不過要注意倒數(shù)第二個(gè)和最后一個(gè)輸出的結(jié)果(如果數(shù)值是int類型的,裝箱過程調(diào)用的是Integer.valueOf笑诅;如果是long類型的调缨,裝箱調(diào)用的Long.valueOf方法)疮鲫。
17、java8的新特性:
1)并行流弦叶,流可以是順序的也可以是并行的俊犯。順序流的操作是在單線程上執(zhí)行的,而并行流的操作是在多線程上并發(fā)執(zhí)行的湾蔓。并行排序所花費(fèi)的時(shí)間大約是順序排序的一半
2)lumbda表達(dá)式瘫析,匿名內(nèi)部類,集合排序
3)接口的default方法和靜態(tài)方法
4)map新方法 -合并map中的實(shí)體默责,maps不支持流贬循。然而現(xiàn)在maps包括了許多新的非常有用的方法用于執(zhí)行通用任務(wù): /forEach使用consumer來對map中的每個(gè)元素進(jìn)行操作,執(zhí)行通用任務(wù)
18) 通過關(guān)鍵字::來傳遞方法和構(gòu)造函數(shù)的引用
18、 Java動(dòng)態(tài)綁定總結(jié) http://www.cnblogs.com/lyp3314/archive/2013/01/26/2877205.html
19桃序、 Java堆和棧詳解 http://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html
看《深入理解Java虛擬機(jī)》更詳細(xì)
棧內(nèi)存主要用于存放局部變量和方法調(diào)用杖虾。局部變量指的是那些在方法中定義的基本類型變量和引用類型變量(即對象的引用變量)。當(dāng)在一段代碼塊中定義一個(gè)變量時(shí)媒熊,Java就會在棧中為這個(gè)變量分配內(nèi)存空間奇适,當(dāng)超過變量的作用域后,Java會自動(dòng)釋放掉為該變量分配的內(nèi)存空間芦鳍,該內(nèi)存空間可以立刻被另作他用嚷往。
堆內(nèi)存主要用于存放對象(在Java中,數(shù)組也是對象)柠衅。在堆中分配的內(nèi)存皮仁,由JVM的自動(dòng)垃圾回收器來管理。在堆中產(chǎn)生一個(gè)對象后菲宴,還可以在棧中定義一個(gè)特殊的變量贷祈,這個(gè)變量的取值等于該對象在堆內(nèi)存中的首地址,在棧中的這個(gè)特殊的變量就是我們常說到的“對象的引用變量”喝峦。
·棧和堆的區(qū)別
Java中棧和堆的區(qū)別自然是面試中的常見問題势誊,下面幾點(diǎn)就是其具體的區(qū)別。
1)各司其職
最主要的區(qū)別就是棧內(nèi)存是用來存儲局部變量和方法調(diào)用谣蠢。而堆內(nèi)存是用來存儲Java中的對象粟耻。引用變量指向的對象都存儲在堆內(nèi)存中。
2)獨(dú)有還是共享
棧內(nèi)存歸屬于單個(gè)線程眉踱,每個(gè)線程都會有一個(gè)棧內(nèi)存勋颖,其存儲的變量只能在其所屬線程中可見,即棧內(nèi)存可以理解成線程的私有內(nèi)存勋锤。而堆內(nèi)存中的對象對所有線程可見饭玲,堆內(nèi)存中的對象可以被所有線程訪問,是共享的叁执。
3)異常錯(cuò)誤
如果棧內(nèi)存沒有可用的空間用來存儲方法調(diào)用和局部變量茄厘,那么JVM會拋出java.lang.StackOverFlowError矮冬。而如果是堆內(nèi)存沒有可用的空間來存儲生成的對象,那么JVM會拋出java.lang.OutOfMemoryError次哈。
4)空間大小
棧內(nèi)存的存儲空間要遠(yuǎn)遠(yuǎn)小于堆內(nèi)存胎署。如果你使用遞歸的話,那么你的棧很快就會被填滿窑滞。如果遞歸沒有及時(shí)跳出琼牧,很可能發(fā)生StackOverFlowError異常。你可以通過-Xss選項(xiàng)設(shè)置棧內(nèi)存的大小哀卫。-Xms選項(xiàng)可以設(shè)置堆的開始時(shí)的大小和堆的最大值巨坊。
在堆中產(chǎn)生了一個(gè)數(shù)組或?qū)ο蠛螅€可以在棧中定義一個(gè)特殊的變量此改,讓棧中這個(gè)變量的取值等于數(shù)組或?qū)ο笤诙褍?nèi)存中的首地址趾撵,棧中的這個(gè)變量就成了數(shù)組或?qū)ο蟮囊米兞俊?/p>
16、 JDK 源代碼研究 Hash 存儲機(jī)制 http://www.ibm.com/developerworks/cn/java/j-lo-hash/
17共啃、 JVM內(nèi)存管理占调, 新生代, 舊生代 http://blog.sina.com.cn/s/blog_55ba8b4601014nzm.html
·JVM內(nèi)存組成結(jié)構(gòu)
JVM內(nèi)存結(jié)構(gòu)由堆移剪、棧究珊、本地方法棧、方法區(qū)等部分組成纵苛,結(jié)構(gòu)圖如下所示:
1)堆
所有通過new創(chuàng)建的對象的內(nèi)存都在堆中分配剿涮,其大小可以通過-Xmx和-Xms來控制。堆被劃分為新生代和舊生代赶站,新生代又被進(jìn)一步劃分為Eden和Survivor區(qū),最后Survivor由FromSpace和ToSpace組成纺念,結(jié)構(gòu)圖如下所示:
新生代贝椿。新建的對象都是用新生代分配內(nèi)存,Eden空間不足的時(shí)候陷谱,會把存活的對象轉(zhuǎn)移到Survivor中烙博,新生代大小可以由-Xmn來控制,也可以用-XX:SurvivorRatio來控制Eden和Survivor的比例舊生代烟逊。用于存放新生代中經(jīng)過多次垃圾回收仍然存活的對象
2)棧
每個(gè)線程執(zhí)行每個(gè)方法的時(shí)候都會在棧中申請一個(gè)棧幀渣窜,每個(gè)棧幀包括局部變量區(qū)和操作數(shù)棧,用于存放此次方法調(diào)用過程中的臨時(shí)變量宪躯、參數(shù)和中間結(jié)果
3)本地方法棧
用于支持native方法的執(zhí)行乔宿,存儲了每個(gè)native方法調(diào)用的狀態(tài)
4)方法區(qū)
存放了要加載的類信息、靜態(tài)變量访雪、final類型的常量详瑞、屬性和方法信息掂林。JVM用持久代(PermanetGeneration)來存放方法區(qū),可通過-XX:PermSize和-XX:MaxPermSize來指定最小值和最大值坝橡。介紹完了JVM內(nèi)存組成結(jié)構(gòu)泻帮,下面我們再來看一下JVM垃圾回收機(jī)制。
·JVM內(nèi)存管理和JVM垃圾回收機(jī)制
JVM垃圾回收機(jī)制
JVM分別對新生代和舊生代采用不同的垃圾回收機(jī)制
新生代的GC:
新生代通常存活時(shí)間較短计寇,因此基于Copying算法來進(jìn)行回收锣杂,所謂Copying算法就是掃描出存活的對象,并復(fù)制到一塊新的完全未使用的空間中番宁,對應(yīng)于新生代元莫,就是在Eden和FromSpace或ToSpace之間copy吩跋。新生代采用空閑指針的方式來控制GC觸發(fā)赞警,指針保持最后一個(gè)分配的對象在新生代區(qū)間的位置招盲,當(dāng)有新的對象要分配內(nèi)存時(shí)糊治,用于檢查空間是否足夠伙判,不夠就觸發(fā)GC癣猾。當(dāng)連續(xù)分配對象時(shí)碰酝,對象會逐漸從eden到survivor梧乘,最后到舊生代离陶,
用javavisualVM來查看稼虎,能明顯觀察到新生代滿了后,會把對象轉(zhuǎn)移到舊生代招刨,然后清空繼續(xù)裝載霎俩,當(dāng)舊生代也滿了后,就會報(bào)outofmemory的異常沉眶,如下圖所示:
在執(zhí)行機(jī)制上JVM提供了串行GC(SerialGC)打却、并行回收GC(ParallelScavenge)和并行GC(ParNew)
1)串行GC
在整個(gè)掃描和復(fù)制過程采用單線程的方式來進(jìn)行,適用于單CPU谎倔、新生代空間較小及對暫停時(shí)間要求不是非常高的應(yīng)用上柳击,是client級別默認(rèn)的GC方式,可以通過-XX:+UseSerialGC來強(qiáng)制指定
2)并行回收GC
在整個(gè)掃描和復(fù)制過程采用多線程的方式來進(jìn)行片习,適用于多CPU捌肴、對暫停時(shí)間要求較短的應(yīng)用上,是server級別默認(rèn)采用的GC方式藕咏,可用-XX:+UseParallelGC來強(qiáng)制指定状知,用-XX:ParallelGCThreads=4來指定線程數(shù)
3)并行GC
與舊生代的并發(fā)GC配合使用
舊生代的GC:
舊生代與新生代不同,對象存活的時(shí)間比較長孽查,比較穩(wěn)定饥悴,因此采用標(biāo)記(Mark)算法來進(jìn)行回收,所謂標(biāo)記就是掃描出存活的對象,然后再進(jìn)行回收未被標(biāo)記的對象铺坞,回收后對用空出的空間要么進(jìn)行合并起宽,要么標(biāo)記出來便于下次進(jìn)行分配,總之就是要減少內(nèi)存碎片帶來的效率損耗济榨。在執(zhí)行機(jī)制上JVM提供了串行GC(SerialMSC)坯沪、并行GC(parallelMSC)和并發(fā)GC(CMS),具體算法細(xì)節(jié)還有待進(jìn)一步深入研究擒滑。
以上各種GC機(jī)制是需要組合使用的腐晾,指定方式由下表所示:
垃圾收集算法:
· 標(biāo)記清除算法:效率不高,會產(chǎn)生大量碎片丐一,標(biāo)記所有要回收的對象藻糖,再清除
· 復(fù)制算法:將內(nèi)存劃分為相等的兩塊,每次只使用一塊库车,每次內(nèi)存用完后巨柒,便將存活著的對象復(fù)制到另一塊。效率高柠衍,實(shí)現(xiàn)簡單洋满;浪費(fèi)一般空間
· 標(biāo)記-整理算法:將所存活的對象都像一段移動(dòng)
· 分代收集算法:新生代每次都有大批對象死去,只有少量存活珍坊,選用“復(fù)制算法”牺勾;老年代存活率高,采用“標(biāo)記-清除”或“標(biāo)記-整理”算法阵漏。
18驻民、 圖解Tomcat 與jvm 類加載機(jī)制 http://www.cnblogs.com/xing901022/p/4574961.html
JVM類加載:
當(dāng)JVM運(yùn)行過程中,用戶需要加載某些類時(shí)履怯,會按照下面的步驟(父類委托機(jī)制):
1 用戶自己的類加載器回还,把加載請求傳給父加載器,父加載器再傳給其父加載器叹洲,一直到加載器樹的頂層柠硕。
2 最頂層的類加載器首先針對其特定的位置加載,如果加載不到就轉(zhuǎn)交給子類疹味。
3 如果一直到底層的類加載都沒有加載到仅叫,那么就會拋出異常ClassNotFoundException帜篇。
19糙捺、 Java異常處理和設(shè)計(jì) http://www.cnblogs.com/dolphin0520/p/3769804.html
Error是無法處理的異常,比如OutOfMemoryError笙隙,一般發(fā)生這種異常洪灯,JVM會選擇終止程序。因此我們編寫程序時(shí)不需要關(guān)心這類異常竟痰。
unchecked exception(非檢查異常)签钩,也稱運(yùn)行時(shí)異常(RuntimeException)掏呼,比如常見的NullPointerException、IndexOutOfBoundsException铅檩。對于運(yùn)行時(shí)異常憎夷,java編譯器不要求必須進(jìn)行異常捕獲處理或者拋出聲明,由程序員自行決定昧旨。
checked exception(檢查異常)拾给,也稱非運(yùn)行時(shí)異常(運(yùn)行時(shí)異常以外的異常就是非運(yùn)行時(shí)異常),java編譯器強(qiáng)制程序員必須進(jìn)行捕獲處理兔沃,比如常見的IOExeption和SQLException蒋得。對于非運(yùn)行時(shí)異常如果不進(jìn)行捕獲或者拋出聲明處理,編譯都不會通過乒疏。
1)Try,catch,finally
·try...catch...; try....finally......; try....catch...finally...
·在使用try..catch..finally塊的時(shí)候额衙,注意千萬不要在finally塊中使用return,因?yàn)閒inally中的return會覆蓋已有的返回值怕吴。
2)throws,throw
throws表示出現(xiàn)異常的一種可能性窍侧,并不一定會發(fā)生這些異常;throw則是拋出了異常械哟,執(zhí)行throw則一定拋出了某種異常對象疏之。
20、 B樹暇咆、B-樹锋爪、B+樹、B*樹 http://www.cnblogs.com/oldhorse/archive/2009/11/16/1604009.html
注意:B-樹==B樹爸业,兩者是一樣的其骄。它是一種平衡的多叉樹,稱為B樹(或B-樹扯旷、B_樹)拯爽。
B-樹是一種多路搜索樹(并不一定是二叉的)
M為樹的階數(shù),B-樹或?yàn)榭諛渚觯駝t滿足下列條件:
1.定義任意非葉子結(jié)點(diǎn)最多只有M個(gè)兒子毯炮;且M>2;
2.根結(jié)點(diǎn)的兒子數(shù)為[2, M]耸黑;
3.除根結(jié)點(diǎn)以外的非葉子結(jié)點(diǎn)的兒子數(shù)為[M/2, M]桃煎;
4.每個(gè)結(jié)點(diǎn)存放至少M(fèi)/2-1(取上整)和至多M-1個(gè)關(guān)鍵字;(至少2個(gè)關(guān)鍵字,根節(jié)點(diǎn)至少一個(gè)關(guān)鍵字)大刊;
5.非葉子結(jié)點(diǎn)的關(guān)鍵字個(gè)數(shù)=指向兒子的指針個(gè)數(shù)-1为迈;
6.非葉子結(jié)點(diǎn)的關(guān)鍵字:K[1], K[2], …, K[m-1],m<M+1;且K[i]< K[i+1] 葫辐;
7.非葉子結(jié)點(diǎn)的指針:P[1], P[2], …, P[m]搜锰;其中P[1]指向關(guān)鍵字小于K[1]的子樹,P[m]指向關(guān)鍵字大于K[m-1]的子樹耿战,其它P[i]指向關(guān)鍵字屬于(K[i-1], K[i])的子樹蛋叼;
8.所有葉子結(jié)點(diǎn)位于同一層;
M=3的B-樹
B-樹的搜索剂陡,從根結(jié)點(diǎn)開始鸦列,對結(jié)點(diǎn)內(nèi)的關(guān)鍵字(有序)序列進(jìn)行二分查找,如果命中則結(jié)束鹏倘,否則進(jìn)入查詢關(guān)鍵字所屬范圍的兒子結(jié)點(diǎn)薯嗤;重復(fù),直到所對應(yīng)的兒子指針為空纤泵,或已經(jīng)是葉子結(jié)點(diǎn)骆姐。
B-樹的特性:
1.關(guān)鍵字集合分布在整顆樹中;
2.任何一個(gè)關(guān)鍵字出現(xiàn)且只出現(xiàn)在一個(gè)結(jié)點(diǎn)中捏题;
3.搜索有可能在非葉子結(jié)點(diǎn)結(jié)束玻褪;
4.其搜索性能等價(jià)于在關(guān)鍵字全集內(nèi)做一次二分查找;
5.自動(dòng)層次控制公荧;
由于限制了除根結(jié)點(diǎn)以外的非葉子結(jié)點(diǎn)带射,至少含有M/2個(gè)兒子,確保了結(jié)點(diǎn)的至少利用率
B+樹
B+樹是B-樹的變體循狰,也是一種多路搜索樹:
1.其定義基本與B-樹同窟社,除了:
2.非葉子結(jié)點(diǎn)的子樹指針與關(guān)鍵字個(gè)數(shù)相同;
3.非葉子結(jié)點(diǎn)的子樹指針P[i]绪钥,指向關(guān)鍵字值屬于[K[i], K[i+1])的子樹
(B-樹是開區(qū)間)灿里;
5.為所有葉子結(jié)點(diǎn)增加一個(gè)鏈指針;
6.所有關(guān)鍵字都在葉子結(jié)點(diǎn)出現(xiàn)程腹;
如:(M=3)
B+的搜索與B-樹也基本相同匣吊,區(qū)別是B+樹只有達(dá)到葉子結(jié)點(diǎn)才命中(B-樹可以在
非葉子結(jié)點(diǎn)命中),其性能也等價(jià)于在關(guān)鍵字全集做一次二分查找寸潦;
B+的特性:
1.所有關(guān)鍵字都出現(xiàn)在葉子結(jié)點(diǎn)的鏈表中(稠密索引)色鸳,且鏈表中的關(guān)鍵字恰好
是有序的;
2.不可能在非葉子結(jié)點(diǎn)命中见转;
3.非葉子結(jié)點(diǎn)相當(dāng)于是葉子結(jié)點(diǎn)的索引(稀疏索引)命雀,葉子結(jié)點(diǎn)相當(dāng)于是存儲
(關(guān)鍵字)數(shù)據(jù)的數(shù)據(jù)層;
4.更適合文件索引系統(tǒng)池户;
B*樹
是B+樹的變體咏雌,在B+樹的非根和非葉子結(jié)點(diǎn)再增加指向兄弟的指針;
B樹定義了非葉子結(jié)點(diǎn)關(guān)鍵字個(gè)數(shù)至少為(2/3)M校焦,即塊的最低使用率為2/3
(代替B+樹的1/2)赊抖;
B+樹的分裂:當(dāng)一個(gè)結(jié)點(diǎn)滿時(shí),分配一個(gè)新的結(jié)點(diǎn)寨典,并將原結(jié)點(diǎn)中1/2的數(shù)據(jù)
復(fù)制到新結(jié)點(diǎn)氛雪,最后在父結(jié)點(diǎn)中增加新結(jié)點(diǎn)的指針;B+樹的分裂只影響原結(jié)點(diǎn)和父
結(jié)點(diǎn)耸成,而不會影響兄弟結(jié)點(diǎn)报亩,所以它不需要指向兄弟的指針;
B樹的分裂:當(dāng)一個(gè)結(jié)點(diǎn)滿時(shí)井氢,如果它的下一個(gè)兄弟結(jié)點(diǎn)未滿弦追,那么將一部分
數(shù)據(jù)移到兄弟結(jié)點(diǎn)中,再在原結(jié)點(diǎn)插入關(guān)鍵字花竞,最后修改父結(jié)點(diǎn)中兄弟結(jié)點(diǎn)的關(guān)鍵字
(因?yàn)樾值芙Y(jié)點(diǎn)的關(guān)鍵字范圍改變了)劲件;如果兄弟也滿了,則在原結(jié)點(diǎn)與兄弟結(jié)點(diǎn)之
間增加新結(jié)點(diǎn)约急,并各復(fù)制1/3的數(shù)據(jù)到新結(jié)點(diǎn)零远,最后在父結(jié)點(diǎn)增加新結(jié)點(diǎn)的指針;
所以厌蔽,B樹分配新結(jié)點(diǎn)的概率比B+樹要低牵辣,空間使用率更高;
總結(jié):
B-樹:多路搜索樹奴饮,每個(gè)結(jié)點(diǎn)存儲M/2到M個(gè)關(guān)鍵字纬向,非葉子結(jié)點(diǎn)存儲指向關(guān)鍵
字范圍的子結(jié)點(diǎn);
所有關(guān)鍵字在整顆樹中出現(xiàn)戴卜,且只出現(xiàn)一次罢猪,非葉子結(jié)點(diǎn)可以命中;
B+樹:在B-樹基礎(chǔ)上叉瘩,為葉子結(jié)點(diǎn)增加鏈表指針膳帕,所有關(guān)鍵字都在葉子結(jié)點(diǎn)
中出現(xiàn),非葉子結(jié)點(diǎn)作為葉子結(jié)點(diǎn)的索引薇缅;B+樹總是到葉子結(jié)點(diǎn)才命中危彩;
B*樹:在B+樹基礎(chǔ)上,為非葉子結(jié)點(diǎn)也增加鏈表指針泳桦,將結(jié)點(diǎn)的最低利用率
從1/2提高到2/3汤徽;
21、 死鎖產(chǎn)生的原因和如何解決灸撰?
死鎖:指多個(gè)進(jìn)程因競爭共享資源而造成的一種僵局谒府,若無外力作用拼坎,這些進(jìn)程都將永遠(yuǎn)不能再 向前推進(jìn)。
安全狀態(tài)與不安全狀態(tài):安全狀態(tài)指系統(tǒng)能按某種進(jìn)程順序來為每個(gè)進(jìn)程分配其所需資源完疫,直至最大需求泰鸡,使每個(gè)進(jìn)程都可順利完成。若系統(tǒng)不存在這樣一個(gè)序列壳鹤, 則稱系統(tǒng)處于不安全狀態(tài)盛龄。
產(chǎn)生死鎖的原因:(1)競爭系統(tǒng)資源 (2)進(jìn)程的推進(jìn)順序不當(dāng)
產(chǎn)生死鎖的必要條件:
互斥條件:進(jìn)程要求對所分配的資源進(jìn)行排它性控制,即在一段時(shí)間內(nèi)某資源僅為一進(jìn)程所占用芳誓。
請求和保持條件:當(dāng)進(jìn)程因請求資源而阻塞時(shí)余舶,對已獲得的資源保持不放。
不剝奪條件:進(jìn)程已獲得的資源在未使用完之前锹淌,不能剝奪匿值,只能在使用完時(shí)由自己釋放。
環(huán)路等待條件:在發(fā)生死鎖時(shí)赂摆,必然存在一個(gè)進(jìn)程--資源的環(huán)形鏈千扔。
解決死鎖的基本方法:
預(yù)防死鎖:
資源一次性分配:(破壞請求和保持條件)
可剝奪資源:即當(dāng)某進(jìn)程新的資源未滿足時(shí),釋放已占有的資源(破壞不可剝奪條件)
資源有序分配法:系統(tǒng)給每類資源賦予一個(gè)編號库正,每一個(gè)進(jìn)程按編號遞增的順序請求資源曲楚,釋放則相反(破壞環(huán)路等待條件)
避免死鎖:
預(yù)防死鎖的幾種策略,會嚴(yán)重地?fù)p害系統(tǒng)性能褥符。因此在避免死鎖時(shí)龙誊,要施加較弱的限制,從而獲得 較滿意的系統(tǒng)性能喷楣。由于在避免死鎖的策略中趟大,允許進(jìn)程動(dòng)態(tài)地申請資源。因而铣焊,系統(tǒng)在進(jìn)行資源分配之前預(yù)先計(jì)算資源分配的安全性逊朽。若此次分配不會導(dǎo)致系統(tǒng)進(jìn)入不安全狀態(tài),則將資源分配給進(jìn)程曲伊;否則叽讳,進(jìn)程等待。其中最具有代表性的避免死鎖算法是銀行家算法坟募。
解除死鎖:
當(dāng)發(fā)現(xiàn)有進(jìn)程死鎖后岛蚤,便應(yīng)立即把它從死鎖狀態(tài)中解脫出來,常采用的方法有:
剝奪資源:從其它進(jìn)程剝奪足夠數(shù)量的資源給死鎖進(jìn)程懈糯,以解除死鎖狀態(tài)涤妒;
撤消進(jìn)程:可以直接撤消死鎖進(jìn)程或撤消代價(jià)最小的進(jìn)程,直至有足夠的資源可用赚哗,死鎖狀態(tài).消除為止她紫;所謂代價(jià)是指優(yōu)先級硅堆、運(yùn)行代價(jià)、進(jìn)程的重要性和價(jià)值等贿讹。
22渐逃、如何檢測兩個(gè)線程死鎖,或者說找出死鎖的線程
有兩個(gè)容器围详,一個(gè)用于保存線程正在請求的鎖,一個(gè)用于保存線程已經(jīng)持有的鎖祖屏。每次加鎖之前都會做如下檢測:
1)檢測當(dāng)前正在請求的鎖是否已經(jīng)被其它線程持有,如果有助赞,則把那些線程找出來。
2)遍歷第一步中返回的線程袁勺,檢查自己持有的鎖是否正被其中任何一個(gè)線程請求
如果第二步返回真,表示出現(xiàn)了死鎖雹食。
23、進(jìn)程與線程的區(qū)別和聯(lián)系期丰,進(jìn)程執(zhí)行的方式群叶。
進(jìn)程概念
進(jìn)程是表示資源分配的基本單位,又是調(diào)度運(yùn)行的基本單位钝荡。例如街立,用戶運(yùn)行自己的程序,系統(tǒng)就創(chuàng)建一個(gè)進(jìn)程埠通,并為它分配資源赎离,包括各種表格、內(nèi)存空間端辱、磁盤空間梁剔、I/O設(shè)備等。然后舞蔽,把該進(jìn)程放人進(jìn)程的就緒隊(duì)列荣病。進(jìn)程調(diào)度程序選中它,為它分配CPU以及其它有關(guān)資源渗柿,該進(jìn)程才真正運(yùn)行个盆。所以,進(jìn)程是系統(tǒng)中的并發(fā)執(zhí)行的單位朵栖。
線程概念
線程是進(jìn)程中執(zhí)行運(yùn)算的最小單位砾省,亦即執(zhí)行處理機(jī)調(diào)度的基本單位。如果把進(jìn)程理解為在邏輯上操作系統(tǒng)所完成的任務(wù)混槐,那么線程表示完成該任務(wù)的許多可能的子任務(wù)之一
進(jìn)程和線程的關(guān)系
(1)一個(gè)線程只能屬于一個(gè)進(jìn)程编兄,而一個(gè)進(jìn)程可以有多個(gè)線程,但至少有一個(gè)線程声登。線程是操作系統(tǒng)可識別的最小執(zhí)行和調(diào)度單位狠鸳。
(2)資源分配給進(jìn)程揣苏,同一進(jìn)程的所有線程共享該進(jìn)程的所有資源。 同一進(jìn)程中的多個(gè)線程共享代碼段(代碼和常量)件舵,數(shù)據(jù)段(全局變量和靜態(tài)變量)卸察,擴(kuò)展段(堆存儲)。但是每個(gè)線程擁有自己的棧段铅祸,棧段又叫運(yùn)行時(shí)段坑质,用來存放所有局部變量和臨時(shí)變量。
(3)處理機(jī)分給線程临梗,即真正在處理機(jī)上運(yùn)行的是線程涡扼。
(4)線程在執(zhí)行過程中,需要協(xié)作同步盟庞。不同進(jìn)程的線程間要利用消息通信的辦法實(shí)現(xiàn)同步吃沪。
24、jvm虛擬機(jī)溢出都是發(fā)生在哪個(gè)地方什猖?什么情況下會發(fā)生溢出票彪?原因何在?
http://www.cnblogs.com/time-info/p/4514164.html
·內(nèi)存泄露與內(nèi)存溢出
內(nèi)存泄露一般是代碼設(shè)計(jì)存在缺陷導(dǎo)致的不狮,指程序中動(dòng)態(tài)分配內(nèi)存給一些臨時(shí)對象降铸,但是對象不會被GC所回收,它始終占用內(nèi)存摇零。即被分配的對象可達(dá)但是已經(jīng)無用垮耳;通過了解內(nèi)存泄露的場景,可以避免不必要的內(nèi)存溢出和提高自己的代碼水平遂黍;
·內(nèi)存泄露的幾種場景:
1终佛、長生命周期的對象持有短生命周期對象的引用
例如:在全局靜態(tài)map中緩存局部變量,且沒有清空操作雾家,隨著時(shí)間的推移铃彰,這個(gè)map會越來越大,造成內(nèi)存泄露芯咧;
2牙捉、修改hashset中對象的參數(shù)值,且參數(shù)是計(jì)算哈希值的字段
當(dāng)一個(gè)對象被存儲進(jìn)HashSet集合中以后敬飒,就不能修改這個(gè)對象中的那些參與計(jì)算哈希值的字段邪铲,否則對象修改后的哈希值與最初存儲進(jìn)HashSet集合中時(shí)的哈希值就不同了,在這種情況下无拗,即使在contains方法中使用該對象的當(dāng)前引用作為參數(shù)去HashSet集合中檢索對象带到,也將返回找不到對象的結(jié)果,這也會導(dǎo)致無法從HashSet集合中刪除當(dāng)前對象英染,造成內(nèi)存泄露揽惹;
3被饿、機(jī)器的連接數(shù)和關(guān)閉時(shí)間設(shè)置;長時(shí)間開啟非常耗費(fèi)資源的連接搪搏,也會造成內(nèi)存泄漏狭握。
內(nèi)存溢出指程序運(yùn)行過程中無法申請到足夠的內(nèi)存而導(dǎo)致的一種錯(cuò)誤。內(nèi)存溢出通常發(fā)生于Old段或者Perm段垃圾回收后疯溺,仍然無內(nèi)存空間容納新的Java對象的情況论颅;通過了解內(nèi)存溢出的集中常見情況,可以在出現(xiàn)內(nèi)存溢出的時(shí)候快速定位問題的位置囱嫩,縮短解決故障的時(shí)間恃疯。
·內(nèi)存溢出的幾種情況:
1、堆內(nèi)存溢出(OutOfMemoryError:java heap space)
Java堆用于存儲對象實(shí)例挠说,只要不斷地創(chuàng)建對象澡谭,并且保證GC Roots到對象之間有可達(dá)路徑來避免垃圾回收機(jī)制清除這些對象愿题,那么在對象數(shù)量到達(dá)最大堆的容量限制后就會產(chǎn)生內(nèi)存溢出異常损俭;
在JVM規(guī)范中,堆中的內(nèi)存是用來存放對象實(shí)例和數(shù)組的潘酗,如果細(xì)分杆兵,堆內(nèi)存可以分為年輕代和年老代,年輕代又是包含1個(gè)Eden區(qū)和2個(gè)Survivor區(qū)仔夺;
當(dāng)生成新對象時(shí)琐脏,內(nèi)存的申請過程如下:
1.1:JVM先嘗試在Eden區(qū)分為新對象所需的內(nèi)存,如果內(nèi)存大小足夠缸兔,那么申請結(jié)束日裙;
1.2:如果Eden區(qū)的內(nèi)存不夠新對象所需的內(nèi)存,JVM會啟動(dòng)Minor GC惰蜜,試圖將Eden區(qū)中不活躍的對象釋放掉昂拂,釋放后若Eden區(qū)仍然不足存放新的對象,則會試圖將Eden中活躍的對象放入Survivor區(qū)抛猖;
1.3:Survivor區(qū)被用來作為Eden區(qū)和Old區(qū)的中間交換區(qū)域格侯,當(dāng)Old區(qū)空間足夠,Survivor區(qū)的對象會被移到Old區(qū)财著,否則會被保留在Survivor區(qū)联四;
1.4:若Old區(qū)的空間不足,那么會觸發(fā)一次Mijor GC(Full GC)
1.5:Full GC后撑教,若Survivor以及Old區(qū)仍然無法存放從Eden復(fù)制過來的部分對象朝墩,導(dǎo)致JVM無法在Eden區(qū)為新對象創(chuàng)建內(nèi)存區(qū)域,則會出現(xiàn)“OutOrMermoryError”錯(cuò)誤伟姐;
2鱼辙、虛擬機(jī)棧和本地方法棧溢出
在HotSpot虛擬機(jī)中是不區(qū)分虛擬機(jī)棧和本地方法棧廉嚼,因此對于HotSpot來說,雖然-Xoss參數(shù)(設(shè)置本地方法棧)存在倒戏,實(shí)際上此參數(shù)是無效的怠噪,棧容量只由-Xss參數(shù)設(shè)置。
Java虛擬機(jī)規(guī)范中對虛擬機(jī)棧和本地方法棧定義了2個(gè)異常:
2.1 如果線程請求的棧深度大于虛擬機(jī)所允許的棧深度杜跷,會拋出StackOverflowError異常傍念;
2.2 如果虛擬機(jī)在擴(kuò)展時(shí),無法申請到足夠的內(nèi)存空間葛闷,會拋出OutOfMemoryError異常憋槐;
單線程中測試,無論是棧深度大于虛擬機(jī)允許的棧深度淑趾,還是無法申請到足夠的內(nèi)存空間阳仔,都是拋出StackOverflowError異常;
1.使用-Xss參數(shù)減小棧內(nèi)存容量扣泊,拋出StackOverflowError異常近范;2.定義大量的本地變量,增大此方法幀本地變量表的長度延蟹,拋出StackOverflowError異常评矩;
如果測試不限于單線程,通過不斷建立線程的方式倒是可以產(chǎn)生內(nèi)存溢出異常阱飘;
虛擬機(jī)提供了參數(shù)來控制Java堆和方法區(qū)這兩個(gè)部分內(nèi)存的最大值斥杜。
剩余的內(nèi)存-Xmx(最大堆容量)-MaxPermSize(最大方法區(qū)容量),程序計(jì)數(shù)器消耗內(nèi)存較小沥匈。如果虛擬機(jī)進(jìn)程本身消耗的內(nèi)存不計(jì)算在內(nèi)蔗喂,那么剩余的內(nèi)存就是被虛擬機(jī)棧和本地方法棧瓜分了。
每個(gè)線程分配到的棧容量越大高帖,可以建立的線程數(shù)量自然越少缰儿,建立線程時(shí)就容易把剩下的內(nèi)存耗盡;
如果是建立過多線程導(dǎo)致內(nèi)存溢出棋恼,在不能減少線程數(shù)或者跟換64位虛擬機(jī)的情況下返弹,就只能通過減少最大堆和減少棧容量來換取更多的線程;
3爪飘、方法區(qū)和運(yùn)行時(shí)常量池溢出(OutOfMemoryError:PermGen space)
運(yùn)行時(shí)常量池是方法區(qū)的一部分义起。
JVM規(guī)范中,方法區(qū)主要是存放類的信息师崎、常量默终、靜態(tài)變量等;
在JDK1.6及之前的版本中,運(yùn)行時(shí)常量池分配在永久代中齐蔽,可以使用-XX:PermSize和-XX:MaxPermSize限制方法區(qū)大小两疚,從而間接限制其中常量池的容量。
在測試運(yùn)行結(jié)果中會看到含滴,運(yùn)行時(shí)常量池溢出诱渤,在OutOfMemoryError后面跟隨的提示信息是PermGen space,說明運(yùn)行時(shí)常量池屬于方法區(qū)(HotSpot虛擬機(jī)中的永久代)的一部分谈况;
jvm參數(shù):-XX:PermSize=2m -XX:MaxPermSize=2m
將方法區(qū)的大小設(shè)置很低即可勺美,在啟動(dòng)加載類庫時(shí)就會出現(xiàn)內(nèi)存不足的情況
方法區(qū)用于存儲Class的相關(guān)信息,如類名碑韵、訪問修飾符赡茸、常量池、字段描述祝闻、方法描述等占卧。對于此區(qū)域的測試,基本思路是運(yùn)行時(shí)產(chǎn)生大量的類去填滿方法區(qū)联喘,直到溢出从诲;
如果程序加載的類過多潮梯,或者使用反射例书、gclib等這種動(dòng)態(tài)代理生成類的技術(shù)昧捷,就可能導(dǎo)致該區(qū)發(fā)生內(nèi)存溢出牲平。
4堤框、線程棧溢出(java.lang.StackOverflowError)
線程棧是線程獨(dú)有的一塊內(nèi)存結(jié)構(gòu),所以線程棧發(fā)生問題必定是某個(gè)線程運(yùn)行時(shí)產(chǎn)生的錯(cuò)誤纵柿。
一般線程棧溢出是由于遞歸太深或方法調(diào)用層級過多導(dǎo)致的蜈抓。
發(fā)生棧溢出的錯(cuò)誤信息為:java.lang.StackOverflowError;
為了避免內(nèi)存泄漏,在編寫代碼的過程中可以參考下面的建議:
1昂儒、盡早釋放無用對象的引用
2沟使、使用字符串處理,避免使用String渊跋,應(yīng)大量使用StringBuffer腊嗡,每個(gè)String對象都得獨(dú)立占用內(nèi)存一塊區(qū)域;
3拾酝、盡量少用靜態(tài)變量燕少,因?yàn)殪o態(tài)變量存在的永久代(方法區(qū)),永久代基本不參與垃圾回收蒿囤;
4客们、避免在循環(huán)中創(chuàng)建對象;
5、開啟大型文件或從數(shù)據(jù)庫一次拿了太多的數(shù)據(jù)很容易造成內(nèi)存溢出底挫,所以在這些地方要大概計(jì)算一下數(shù)據(jù)量的最大值是多少恒傻,并且設(shè)定所需最小及最大的內(nèi)存空間值。
25建邓、equals盈厘、==、hashcode http://blog.csdn.net/hla199106/article/details/46907725
1官边、==
Java中的數(shù)據(jù)類型扑庞,可分為兩類:
1).基本數(shù)據(jù)類型,也稱原始數(shù)據(jù)類型
byte,short,char,int,long,float,double,boolean 他們之間的比較拒逮,應(yīng)用雙等號(==),比較的是他們的值罐氨。
2).引用類型(類、接口滩援、數(shù)組)
當(dāng)他們用(==)進(jìn)行比較的時(shí)候栅隐,比較的是他們在內(nèi)存中的存放地址,所以玩徊,除非是同一個(gè)new出來的對象租悄,他們的比較后的結(jié)果為true,否則比較后結(jié)果為false恩袱。
對象是放在堆中的泣棋,棧中存放的是對象的引用(地址)。由此可見'=='是對棧中的值進(jìn)行比較的畔塔。如果要比較堆中對象的內(nèi)容是否相同潭辈,那么就要重寫equals方法了。
2澈吨、equals
1)把敢、默認(rèn)情況(沒有覆蓋equals方法)下equals方法都是調(diào)用Object類的equals方法,而Object的equals方法主要用于判斷對象的內(nèi)存地址引用是不是同一個(gè)地址(是不是同一個(gè)對象)谅辣。
定義的equals與==是等效的
2)修赞、要是類中覆蓋了equals方法,那么就要根據(jù)具體的代碼來確定equals方法的作用了桑阶,覆蓋后一般都是通過對象的內(nèi)容是否相等來判斷對象是否相等柏副。
3、 hashCode的作用
要想保證元素不重復(fù)蚣录,可兩個(gè)元素是否重復(fù)應(yīng)該依據(jù)什么來判斷呢割择?
這就是Object.equals方法了。但是包归,如果每增加一個(gè)元素就檢查一次锨推,那么當(dāng)元素很多時(shí)铅歼,后添加到集合中的元素比較的次數(shù)就非常多了。也就是說换可,如果集合中現(xiàn)在已經(jīng)有1000個(gè)元素椎椰,那么第1001個(gè)元素加入集合時(shí),它就要調(diào)用1000次equals方法沾鳄。這顯然會大大降低效率慨飘。
于是,Java采用了哈希表的原理译荞。這樣一來瓤的,當(dāng)集合要添加新的元素時(shí),先調(diào)用這個(gè)元素的hashCode方法吞歼,就一下子能定位到它應(yīng)該放置的物理位置上圈膏。 如果這個(gè)位置上沒有元素,它就可以直接存儲在這個(gè)位置上篙骡,不用再進(jìn)行任何比較了稽坤;如果這個(gè)位置上已經(jīng)有元素了,就調(diào)用它的equals方法與新元素進(jìn)行比較糯俗,相同的話就不存尿褪,不相同就散列其它的地址。所以這里存在一個(gè)沖突解決的問題得湘。這樣一來實(shí)際調(diào)用equals方法的次數(shù)就大大降低了杖玲,幾乎只需要一兩次.
4、hashcode和equals:
1)淘正、如果兩個(gè)對象equals摆马,Java運(yùn)行時(shí)環(huán)境會認(rèn)為他們的hashcode一定相等。
2)跪帝、如果兩個(gè)對象不equals今膊,他們的hashcode有可能相等些阅。
3)伞剑、如果兩個(gè)對象hashcode相等,他們不一定equals市埋。
4)黎泣、如果兩個(gè)對象hashcode不相等,他們一定不equals缤谎。
5抒倚、為什么覆蓋equals時(shí)總要覆蓋hashCode
一個(gè)很常見的錯(cuò)誤根源在于沒有覆蓋hashCode方法。在每個(gè)覆蓋了equals方法的類中坷澡,也必須覆蓋hashCode方法托呕。如果不這樣做的話,就會違反Object.hashCode的通用約定,從而導(dǎo)致該類無法結(jié)合所有基于散列的集合一起正常運(yùn)作项郊,這樣的集合包括HashMap馅扣、HashSet和Hashtable。