java源碼學(xué)習(xí)饥追,(基于java version 1.8.0_60)
1.基礎(chǔ)部分(原理咕痛,運(yùn)用熟練掌握)
1.1java.util
這個(gè)包主要以集合為主仿便,這是數(shù)據(jù)結(jié)構(gòu)的最佳實(shí)踐悯仙。
集合羅列
集合 | 查詢時(shí)間復(fù)雜度 | 繼承前列 | 線程安全 | 支持排序 |
---|---|---|---|---|
Map | HashMap | LinkedHashMap | HashTable | TreeMap |
Set | HashSet | LinkedHashSet | TreeSet | |
~ | 無 | 無 | 線程安全 | 繼承前列線程安全 |
List | ArrayList | LinkedList | Vector | Stack |
相似比較
Name | 同作用 | 不同作用 |
---|---|---|
Iterator | 支持遍歷 | 支持fail-fast[ConcurrentModificationException]機(jī)制囱修,通過迭代器可以對(duì)集合刪除;可以對(duì)集合進(jìn)行增強(qiáng)for循環(huán)遍歷【Iterable和Iterator一起使用捞烟,Iterable像管理者薄声,產(chǎn)生Iterator,Iterator負(fù)責(zé)執(zhí)行】 |
Enumeration | 支持遍歷 | |
Map | 支持key-value | 功能強(qiáng)(官方建議使用map) |
Dictionary | 支持key-value | 和map比較功能弱 |
Comparable | 支持比較 | 實(shí)體直接實(shí)現(xiàn) |
Comparator | 支持比較 | 更靈活题画,實(shí)體可以實(shí)現(xiàn)多個(gè) |
1.1.1Map相關(guān)
- 這是集合中個(gè)人認(rèn)為是最關(guān)鍵最復(fù)雜的類默辨,搞清楚map的常用實(shí)現(xiàn)類是非常關(guān)鍵的對(duì)于認(rèn)識(shí)集合總體的實(shí)現(xiàn)。
- 每個(gè)key苍息,value鍵值對(duì)封裝為Map.Entry<K, V> 實(shí)體bean缩幸。
1.1.1.1HashMap
- 數(shù)據(jù)結(jié)構(gòu):Node實(shí)體數(shù)組(又叫Hash表或散列表,hash函數(shù)又叫散列函數(shù))+ 單向鏈表+紅黑樹(又叫自平衡二叉查找樹)竞思。
- Node實(shí)體基本結(jié)構(gòu):hash,key,value,next(鏈表的下一個(gè)實(shí)體);Map.Entry<K, V> 實(shí)現(xiàn)類表谊。
- TreeNode實(shí)體基本結(jié)構(gòu):parent,left,right,prev;TreeNode繼承LinkedHashMap.Entry繼承HashMap.Node<K,V>。
- 數(shù)組上的元素為鏈表(或紅黑樹)的頭節(jié)點(diǎn)(或根節(jié)點(diǎn))盖喷。
- 對(duì)于hash沖突解決辦法使用鏈表(或紅黑樹)爆办,當(dāng)鏈表長(zhǎng)度大于等于8時(shí)轉(zhuǎn)化為紅黑樹。之所以轉(zhuǎn)化為紅黑樹是當(dāng)數(shù)量大時(shí)紅黑樹的查詢效率更高课梳。
- 操作紅黑樹距辆,增加刪除后為了自平衡 進(jìn)行的左右旋和重新著色。
幾種時(shí)間復(fù)雜度
結(jié)構(gòu) | 查詢時(shí)間復(fù)雜度 | 說明 |
---|---|---|
hash表 | O(1) | |
鏈表 | O(n) | 遍歷鏈表去查找 |
紅黑樹 | O(log n) |
多線程下當(dāng)共享變量使用時(shí)暮刃,擴(kuò)容鏈表造成的問題
版本 | 元素是否倒置 | 元素是否丟失 | 復(fù)雜程度 |
---|---|---|---|
1.7 | 倒置(易形成閉合回路) | 沒驗(yàn)證 | 代碼量約1000行 |
1.8 | 不倒置(99%的認(rèn)為不會(huì)形成挑格,1%再驗(yàn)證) | 會(huì)丟失 | 代碼量約2000行 |
解決辦法 | 不當(dāng)共享變量使用(在局部變量中 new創(chuàng)建 是沒問題的,在棧中操作沾歪,屬于私有的)或使用Collections.synchronizedMap(Map<K, V>)包裝或使用ConcurrentHashMap |
1.1.1.2HashTable
- 數(shù)據(jù)結(jié)構(gòu):數(shù)組+單向鏈表漂彤。
- Entry實(shí)體基本結(jié)構(gòu):hash,key,value,next與HsahMap的Node實(shí)體結(jié)構(gòu)一致;Map.Entry<K, V> 實(shí)現(xiàn)類。
- 擴(kuò)容時(shí)元素順序不變。
- 由于HashTable是同步的挫望,多數(shù)方法添加了synchronized關(guān)鍵字同步立润。
1.1.1.3TreeMap
- 數(shù)據(jù)結(jié)構(gòu):紅黑樹(天然排序)。
- Entry結(jié)構(gòu):key媳板,value桑腮, left,right蛉幸,parent破讨,color
- 由于這是排序的hash表,必須在構(gòu)造函數(shù)傳遞比較器Comparator實(shí)現(xiàn)類或添加的實(shí)體實(shí)現(xiàn)Comparable接口
- Comparator和Comparable區(qū)別:Comparable奕纫,方便提陶,實(shí)體直接繼承,不靈活匹层。Comparator隙笆,靈活,根據(jù)業(yè)務(wù)多維度實(shí)現(xiàn)比較
- 實(shí)現(xiàn)結(jié)果基本和HashMap的紅黑樹實(shí)現(xiàn)部分一致
1.1.1.4LinkedHashMap
- 數(shù)據(jù)結(jié)構(gòu):雙向鏈表(輸入的順序和添加順序一致)
- Entry結(jié)構(gòu)升筏,繼承自HashMap.Node ,新添加before, after屬性
- 繼承自HashMap
- 新添加的元素放置到鏈表的尾部撑柔,遍歷時(shí)從頭節(jié)點(diǎn)開始,保證了輸入的順序和添加順序一致
幾種map總結(jié):
1您访、在存儲(chǔ)上基本與value沒有關(guān)系铅忿,主要依賴K和K的hash值來放置到合適位置。
2灵汪、內(nèi)部實(shí)現(xiàn)上基本以循環(huán)操作為主檀训。
1.1.2Set相關(guān)
1.1.2.1HashSet
- 實(shí)現(xiàn)依賴于HashMap
1.1.2.3LinkedHashSet
- 實(shí)現(xiàn)依賴于LinkedHashMap(通過構(gòu)造函數(shù)調(diào)用HashSet的LinkedHashMap構(gòu)造函數(shù)實(shí)現(xiàn))
1.1.2.2TreeSet
- 支持排序,間接實(shí)現(xiàn)SortedSet接口
- 實(shí)現(xiàn)依賴于NavigableMap
1.1.3List相關(guān)
1.1.2.1ArrayList
- 數(shù)據(jù)結(jié)構(gòu):數(shù)組
- 擴(kuò)容依賴Arrays.copyOf()识虚,它又依賴于 System.arraycopy()
- 通過索引訪問遍歷效率最高肢扯,而使用迭代器的效率最低(還可以通過增強(qiáng)for循環(huán)遍歷)
1.1.2.2LinkedList
- 數(shù)據(jù)結(jié)構(gòu):雙向鏈表
- Node實(shí)體:item,next, prev(當(dāng)前,下一個(gè)担锤,上一個(gè))
- LinkedList可以作為FIFO(先進(jìn)先出)的隊(duì)列
- LinkedList可以作為L(zhǎng)IFO(后進(jìn)先出)的棧
1.1.2.3Vector
- 數(shù)據(jù)結(jié)構(gòu):數(shù)組
- 支持同步
1.1.2.3Stack
- 數(shù)據(jù)結(jié)構(gòu):數(shù)組
- 繼承自Vector蔚晨,支持同步,先進(jìn)后出
1.1.4工具類
- Arrays:提供一系列靜態(tài)方法肛循,對(duì)數(shù)組排序铭腕,搜索等
- Collections :提供一系列靜態(tài)方法,對(duì)集合 排序多糠,搜索累舷,包裝集合為安全類型等。包裝基本實(shí)現(xiàn)思路為夹孔,傳入之后賦值給局部變量被盈,通過synchronized關(guān)鍵字同步對(duì)于傳入集合的原有方法加以限制析孽。
1.2java.io
理解類提供的功能,才能更準(zhǔn)確的使用。要理解io必須先了解一些基本的概念和區(qū)別只怎。
- 裝飾模式和適配器模式
- 字符流和字節(jié)流
- 流的基本介質(zhì)和區(qū)別
io字節(jié)輸入流(不是所有的輸入輸出都一一對(duì)應(yīng))
io字節(jié)輸出流
io字符輸入流
io字符輸出流
1.3java.lang
java的base包袜瞬,唯一一個(gè)只使用不用導(dǎo)包的包。
主要類的類型有:基本類型的包裝類型身堡,異常類邓尤,進(jìn)程類,Thread的相關(guān)類贴谎,字符(串)的系列類汞扎。
- Object:主要提供hashcode,equals 擅这,getClass澈魄,以及線程之間通信的方式:等待和喚醒。
- System
- 主要是native方法
- 集成一些其他類像IO流蕾哟,
- 集成RunTime的方法
- nanoTime():獲取微妙值一忱,主要用于線程阻塞時(shí)間的計(jì)算莲蜘。
- arraycopy():高效數(shù)組的拷貝谭确,(包括Arrays.copyOf) 都是拷貝引用
- RunTime:exit,gc票渠,exec逐哈,獲取os運(yùn)行核數(shù)。
- Class:對(duì)象字節(jié)碼對(duì)象问顷,反射的重點(diǎn)
1.4java.util除過集合的其他類
- Formatter:公式 %[argument_index$][flags][width][.precision]conversion
2.高級(jí)部分(深刻理解)
2.1java.lang.reflect
2.1.1反射
- 獲取對(duì)象字節(jié)對(duì)象的3種方式
- this.getClass()
- 對(duì)象.class
- Class.forName() 獲取對(duì)象字節(jié)碼對(duì)象
- Class 對(duì)象可以獲取字段數(shù)組昂秃,方法數(shù)組,構(gòu)造函數(shù)(通過方法可以創(chuàng)建實(shí)例)杜窄,所涉及的權(quán)限修飾符肠骆,參數(shù),參數(shù)類型塞耕,方法的返回值類等一切基本都可以獲取到蚀腿。
2.1.2動(dòng)態(tài)代理
- 代理模式的應(yīng)用
- 創(chuàng)建過程類似于“高級(jí)裝飾者模式”+“高級(jí)繼承(非繼承實(shí)現(xiàn)類,繼承的是接口)”
- 生成代理類過程:生成代理對(duì)象的字節(jié)碼數(shù)組->生成字節(jié)碼對(duì)象->通過構(gòu)造函數(shù)創(chuàng)建實(shí)例
- 代理對(duì)象和被代理對(duì)象的區(qū)別:代理對(duì)象由于是Proxy的子類扫外,所以持有起橋梁作用的InvocationHandler子類(簡(jiǎn)稱h)莉钙,h又持有被代理對(duì),通過代理對(duì)象給h傳入Method對(duì)象和參數(shù)在加上h持有的被代理對(duì)象就可以對(duì)代理對(duì)象通過Method的invoke執(zhí)行筛谚。其實(shí)就是代理對(duì)象持有自己實(shí)現(xiàn)的InvocationHandler對(duì)象磁玉,而被代理對(duì)象是沒有的。(Method的invoke傳入的對(duì)象一定不能是h的invoke方法傳入的proxy對(duì)象驾讲,proxy對(duì)象是代理對(duì)象傳入的自己本身蚊伞,如果傳入Proxy席赂,將會(huì)發(fā)生代理對(duì)象和h對(duì)象之間的遞歸調(diào)用,直到棧溢出时迫,正確應(yīng)該傳入h持有的被代理對(duì)象氧枣。h的invoke方法傳入的proxy對(duì)象也沒有什么實(shí)際作用)
- 代理對(duì)象里面的基本方法有equals,toString别垮,hashCode和接口的方法便监。所有方法里面沒有額外的邏輯,都是通過h.invoke(this, method, 參數(shù))去調(diào)用真是對(duì)象的碳想。通過 ProxyGenerator.generateProxyClass("$Proxy11", 接口字節(jié)碼數(shù)組); 可以寫入生成代理對(duì)象到文件進(jìn)行查看烧董。
- h對(duì)象里面的invoke是對(duì)被代理對(duì)象的所有方法的代理,想要定制化代理可以在里面通過傳入的Method對(duì)象獲取方法名進(jìn)行判斷處理胧奔。
2.2java.util.concurrent.*
并發(fā)包主要內(nèi)容:原子類逊移,鎖,容器龙填,線程池胳泉,框架,工具類
2.2.1原子類
- 關(guān)于Unsafe的并發(fā)性岩遗。compareAndSwap*方法是原子的扇商,并且可用來實(shí)現(xiàn)高性能的、無鎖(free-lock)的數(shù)據(jù)結(jié)構(gòu)宿礁。
可能存在ABA問題案铺、指令重排序等問題 - 獲取Unsafe實(shí)例的2種方法:反射獲取字段的方法或反射獲取構(gòu)造函數(shù)獲取
- 自定義實(shí)現(xiàn)Integer的原子操作類:CAtomicInteger
- 原子類主要提供:3類(int,long梆靖,Reference)控汉,每類3種(本身,數(shù)組返吻,字段)的更新
外加Boolean(底層轉(zhuǎn)化為int),具體實(shí)現(xiàn)對(duì)應(yīng)于Unsafe類的方法 - unsafe.compareAndSwapInt(arg0, arg1, arg2, arg3)
- unsafe.compareAndSwapLong(arg0, arg1, arg2, arg3)
- unsafe.compareAndSwapObject(arg0, arg1, arg2, arg3)
數(shù)組的原子更新是通過索引號(hào) 更新的具體數(shù)值的
若想要實(shí)現(xiàn)其他基本類型姑子,可以通過轉(zhuǎn)化為int 或long類型轉(zhuǎn)化去操作 - 實(shí)現(xiàn)原理大致過程如下:
- 有一些狀態(tài)
- 創(chuàng)建它的副本
- 修改它
- 執(zhí)行CAS
- 如果失敗,重復(fù)嘗試直到成功
無鎖編程(lock free)
常見的lock free編程一般是基于CAS(Compare And Swap)操作:CAS(void ptr, Any oldValue, Any newValue);即查看內(nèi)存地址ptr處的值测僵,如果為oldValue則將其改為 newValue街佑,并返回true,否則返回false恨课。 X86平臺(tái)上的CAS操作一般是通過CPU的CMPXCHG指令來完成的舆乔。CPU在執(zhí)行此指令時(shí)會(huì)首先鎖住CPU總線,禁止其它核心對(duì)內(nèi)存的訪問剂公,然后再查看或修改ptr的值希俩。簡(jiǎn)單的說CAS利用了CPU的硬件鎖來實(shí)現(xiàn)對(duì)共享資源的串行使用。
優(yōu)點(diǎn):
- a纲辽、開銷較醒瘴洹:不需要進(jìn)入內(nèi)核璃搜,不需要切換線程;
- b鳞上、沒有死鎖:總線鎖最長(zhǎng)持續(xù)為一次read+write的時(shí)間这吻;
- c、只有寫操作需要使用CAS篙议,讀操作與串行代碼完全相同唾糯,可實(shí)現(xiàn)讀寫不互斥。
缺點(diǎn):
- a鬼贱、編程非常復(fù)雜移怯,兩行代碼之間可能發(fā)生任何事,很多常識(shí)性的假設(shè)都不成立这难。
- b舟误、CAS模型覆蓋的情況非常少,無法用CAS實(shí)現(xiàn)原子的復(fù)數(shù)操作姻乓。
2.2.2鎖
主要類關(guān)系:
2.2.2.1AQS
- 鎖是面向用戶的嵌溢,同步器是面向鎖的(就是鎖的內(nèi)部實(shí)現(xiàn)),AQS支持互斥鎖蹋岩,共享鎖的實(shí)現(xiàn)赖草。
- AQS實(shí)現(xiàn)一個(gè)FIFO等待隊(duì)列。
- 通過對(duì)state的原子修改來實(shí)現(xiàn)獲取鎖和釋放鎖星澳;
- 互斥鎖:state為1時(shí)可以實(shí)現(xiàn)互斥鎖(TCustomSyncExclusiveLock)疚顷,大于1可以實(shí)現(xiàn)重入鎖旱易;
- 共享鎖:state大于1時(shí)可以實(shí)現(xiàn)共享鎖(TCustomSyncShareLock)禁偎,此時(shí)state的值即表示并發(fā)的線程數(shù),此時(shí)不便實(shí)現(xiàn)重入鎖阀坏。
2.2.2.1.1關(guān)鍵詞理解
模板方法如暖,互斥鎖(排它鎖),共享鎖忌堂,同步隊(duì)列盒至,阻塞隊(duì)列(條件隊(duì)列),LockSupport士修,ConditionObject
- AQS:提供一組模板方法用于具體業(yè)務(wù)實(shí)現(xiàn)互斥鎖或者共享鎖
- 同步隊(duì)列:保存等待獲取鎖的線程節(jié)點(diǎn)
- 阻塞隊(duì)列:保存執(zhí)行了await的線程節(jié)點(diǎn)
- LockSupport:用于阻塞或者喚醒線程枷遂,屬于工具類,不和鎖關(guān)聯(lián)(Condition和synchronized阻止線程必須和鎖關(guān)聯(lián))
- ConditionObject:AQS的監(jiān)視器
(注意:Object的監(jiān)視器只有一個(gè)同步隊(duì)列和一個(gè)阻塞隊(duì)列棋嘲;而AQS卻有一個(gè)同步隊(duì)列和多個(gè)阻塞隊(duì)列酒唉,對(duì)應(yīng)多個(gè)ConditionObject)
2.2.2.1.2同步隊(duì)列和阻塞隊(duì)列的節(jié)點(diǎn)互相轉(zhuǎn)化(結(jié)合ReentrantLock說明)
- TReentrantLock3運(yùn)行示例
- 同步隊(duì)列:雙向鏈表;阻塞隊(duì)列:?jiǎn)蜗蜴湵?/li>
- 基本流程:首先所有節(jié)點(diǎn)會(huì)被加入同步隊(duì)列沸移,在執(zhí)行await后會(huì)加入阻塞隊(duì)列痪伦,喚醒時(shí)又會(huì)被轉(zhuǎn)化回同步隊(duì)列
- 2個(gè)隊(duì)列獨(dú)立存在侄榴,都使用Node作為基本節(jié)點(diǎn)
(注意使線程節(jié)點(diǎn)進(jìn)入阻塞隊(duì)列的不適合AQS實(shí)現(xiàn)的共享鎖操作,因?yàn)閚ewCondition.await()其中會(huì)調(diào)用tryRelease(long arg),而共享鎖不會(huì)實(shí)現(xiàn)這個(gè))
2.2.2.1.3lock ,unLock,await,signal(signalAll:相當(dāng)于循環(huán)操作signal)的邏輯分析(結(jié)合ReentrantLock說明)
- lock:獲取不到鎖阻塞該線程网沾,加入同步隊(duì)列
- unLock:釋放鎖喚醒后繼節(jié)點(diǎn)
- await:主要干三件事:1.阻塞該線程 2.添加到等待隊(duì)列3. 喚醒后繼線程
- signal:主要干1件事:加入同步隊(duì)列
2.2.2.2ReentrantLock
- 主要理解公平鎖和非公平鎖的在獲取鎖時(shí)的不同之處(二者都使用同步隊(duì)列癞蚕,非公平鎖再獲取時(shí)存在插隊(duì)現(xiàn)象,這樣對(duì)于隊(duì)列其他的節(jié)點(diǎn)線程就是不公平的)
- ReentrantLock,屬于互斥鎖辉哥,重入鎖(釋放必須和獲取執(zhí)行次數(shù)一樣)
- ReentrantLock的方法在調(diào)用時(shí) 如果拋出 IllegalMonitorStateException - 則該方法必須在鎖的區(qū)域內(nèi)調(diào)用
- 不管是公平鎖或者非公平鎖都使用的AQS的同步隊(duì)列
- 性能問題:(TReentrantLock2測(cè)試)
- 前提:把不同線程獲取鎖的一次定義為一次上下文切換
- 獲取同等鎖次數(shù)情況下桦山,非公平鎖相對(duì)用時(shí)更少,原因是減少了cpu的上下文切換
- 公平鎖執(zhí)行較多上下文切換次數(shù)醋旦, 而非公平鎖執(zhí)行上下文切換次數(shù)較少(原因是當(dāng)一個(gè)線程獲取釋放鎖后度苔,下一次如果它再需要鎖,相對(duì)比其他線程獲取鎖的概率更大浑度,此時(shí)就不需要切換就相對(duì)省時(shí))
2.2.2.3ReentrantReadWriteLock
- 出現(xiàn)讀寫鎖的緣由:當(dāng)多讀少寫時(shí)寇窑,使用讀寫鎖比使用互斥鎖具有更高的并發(fā)性
- 特點(diǎn):(讀鎖可以并發(fā)訪問,寫鎖時(shí)其他的讀鎖和其他的寫鎖都被阻塞)
- 主要依據(jù)32位的int類型的state的低16位的值表示寫鎖(為互斥鎖)箩张;高16位的值表示讀鎖(為共享鎖)
- 通過位運(yùn)算把state值拆分為2個(gè)值:假設(shè)當(dāng)前狀態(tài)為s
- 寫狀態(tài):s & (1 << 16) - 1 即:s&65535甩骏,也就是s與16個(gè)1(2進(jìn)制的),這樣就把高16位抹去了先慷;當(dāng)寫狀態(tài)+1時(shí)饮笛,即為s+1,只給低位加论熙。
- 讀狀態(tài):s>>>16 ,表示無符號(hào)補(bǔ)0右移16位福青;當(dāng)讀狀態(tài)+1時(shí),等于s+(1<<16),結(jié)果就是只給高位加
- (鎖降級(jí))流程:先獲取寫鎖脓诡,在獲取讀鎖无午,在釋放寫鎖,在釋放讀鎖(不支持鎖升級(jí))
2.2.3容器
主要并發(fā)容器
2.2.3.1隊(duì)列
- 阻塞隊(duì)列主要方法說明:
- [add:remove]沒有值或隊(duì)列滿時(shí)祝谚,操作報(bào)異常宪迟;屬于Queue接口的規(guī)范
- [offer:poll]返回特殊值(null或波爾值),隊(duì)列滿時(shí)添加失敗交惯,造成丟失元素次泽;屬于Queue接口的規(guī)范
- [put:take]空或滿時(shí)操作阻塞,但是不會(huì)丟失元素席爽;屬于BlockingQueue接口的規(guī)范
-
ArrayBlockingQueue:數(shù)組實(shí)現(xiàn)的有節(jié)阻塞隊(duì)列意荤,此隊(duì)列按 FIFO(先進(jìn)先出)原則。
自定義CArrayBlockingQueue注意:- putIndex和takeIndex只锻,當(dāng)?shù)竭_(dá)數(shù)組末端時(shí)玖像,必須從0開始 。
- while和if的選擇炬藤?
- 必須使用while御铃,因?yàn)樵趕ignal()take1線程時(shí)碴里,只會(huì)從阻塞隊(duì)列轉(zhuǎn)移到同步隊(duì)列(不見得會(huì)獲取鎖真正喚醒), 此時(shí)可能已經(jīng)有其他take2線程獲取到了鎖上真,把隊(duì)列中僅有的元素移除了咬腋,此時(shí)take2線程執(zhí)行完后是釋放鎖,喚醒后 繼節(jié)點(diǎn)也就是take1線程睡互。此時(shí)take1線程應(yīng)該再次判斷根竿,條件不滿足繼續(xù)阻塞。
- 能否通過公平鎖或不公平鎖避免這個(gè)問題就珠? 不能寇壳,因?yàn)樯厦娴膖ake2線程可以認(rèn)為是不公平鎖的插隊(duì)線程。 加入是公平鎖呢妻怎?上面 take1線程順利獲取到了鎖壳炎,取走了唯一的元素。而新線程在獲取不到鎖時(shí)逼侦,加入同步隊(duì)列匿辩。當(dāng)線程 take1 線程執(zhí)行完后釋放鎖,take2線程被喚醒榛丢,也必須再次檢測(cè)while條件铲球。否則將返回null元素,不符合阻塞隊(duì)列晰赞。
- 區(qū)別:while比if執(zhí)行完后多一次檢測(cè)
- if:只會(huì)進(jìn)行一次判斷稼病,執(zhí)行if代碼塊完后就會(huì)執(zhí)行if之后的代碼
- while:符合while條件后,執(zhí)行完while代碼塊里面的代碼掖鱼,執(zhí)行完后會(huì)再次檢查while條件然走,符合繼續(xù)執(zhí)行;不符合跳過锨用,while:多使用于多線程喚醒后的再次檢查條件
- signal和signalAll:使用這倆個(gè)都可以丰刊,只不過只會(huì)有一個(gè)線程獲取鎖得到元素,所以使用signal比signalAll更恰當(dāng)
- LinkedBlockingQueue:一個(gè)單向鏈表實(shí)現(xiàn)的有界(可指定大小增拥,默認(rèn)Integer.MAX_VALUE)阻塞隊(duì)列,此隊(duì)列按 FIFO(先進(jìn)先出)原則寻歧。鏈接隊(duì)列的吞吐量通常要高于基于數(shù)組的隊(duì)列掌栅,但是在大多數(shù)并發(fā)應(yīng)用程序中,其可預(yù)知的性能要低码泛。
-
PriorityBlockingQueue:一個(gè)無界阻塞隊(duì)列猾封,雖然此隊(duì)列邏輯上是無界的,但是資源被耗盡時(shí)試圖執(zhí)行 add 操作也將失敗噪珊,導(dǎo)致 OutOfMemoryError晌缘。
不支持先進(jìn)先出原則
- 由于隊(duì)列是無界的齐莲,所以put方法不會(huì)由于滿了(始終不會(huì)滿)而導(dǎo)致阻塞 。
- 不保證具有同等優(yōu)先級(jí)的元素的順序
- 入隊(duì)(通過比較找到合適位置入隊(duì))相同(值相等)元素不會(huì)覆蓋磷箕,出對(duì)從隊(duì)列頭出隊(duì)
- DelayQueue:一個(gè)無界阻塞隊(duì)列选酗,內(nèi)部根據(jù)PriorityQueue(無界線程不安全隊(duì)列) 存儲(chǔ)元素,適用場(chǎng)景:定時(shí)執(zhí)行(獲仍兰稀)芒填,緩存失效等
- LinkedBlockingDeque:鏈表實(shí)現(xiàn)的有界阻塞雙端隊(duì)列,支持同端存取(FILO)和異端存瓤辗薄(FIFO)殿衰。如果未指定容量,那么容量將等于 Integer.MAX_VALUE
-
LinkedTransferQueue:一個(gè)鏈表實(shí)現(xiàn)的無界阻塞隊(duì)列
- tryTransfer()嘗試傳遞給消費(fèi)者 ,沒有消費(fèi)者放入隊(duì)列
- transfer()嘗試傳遞給消費(fèi)者 ,沒有消費(fèi)者自己處于阻塞狀態(tài)盛泡,這種模式類似與SynchronousQueue
- 無論是transfer還是tryTransfer方法闷祥,在>=1個(gè)消費(fèi)者線程等待獲取元素時(shí)(此時(shí)隊(duì)列為空),都會(huì)立刻轉(zhuǎn)交傲诵,這屬于線程之間的元素交換蜀踏。注意,這時(shí)掰吕,元素并沒有進(jìn)入隊(duì)列果覆。
- 在隊(duì)列中已有數(shù)據(jù)情況下,transfer將需要等待前面數(shù)據(jù)被消費(fèi)掉殖熟,直到傳遞的元素e被消費(fèi)線程取走為止局待。
-
SynchronousQueue:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列
- 其中每個(gè)插入操作必須等待另一個(gè)線程的對(duì)應(yīng)移除操作,如果沒被消費(fèi)則一直處于阻塞
- 此同步隊(duì)列沒有任何內(nèi)部容量菱属,甚至連一個(gè)隊(duì)列的容量都沒有,適合傳遞性的應(yīng)用場(chǎng)景
2.2.3.2Copy-On-Write簡(jiǎn)稱COW钳榨,僅2個(gè)類Set和List
- Copy-On-Write簡(jiǎn)稱COW,是一種用于程序設(shè)計(jì)中的優(yōu)化策略纽门,采用數(shù)組存儲(chǔ)薛耻。
- 使用場(chǎng)景:多讀少寫(讀不加鎖,寫會(huì)加鎖赏陵,防止多個(gè)線程多多個(gè)拷貝同時(shí)造成數(shù)據(jù)混亂饼齿,所以會(huì)加鎖防止此問題)
- 寫時(shí)采用復(fù)制新的容器,進(jìn)行修改蝙搔;新容器為原容器中對(duì)象的引用缕溉,使用完把新容器賦值給類的原有容器引用。復(fù)制新的容器時(shí)吃型,這是一個(gè)比較耗費(fèi)性能的操作
- CopyOnWriteArraySet 采用CopyOnWriteArrayList 作為存儲(chǔ)证鸥。基本差不多
- CopyOnWrite的缺點(diǎn)
- 內(nèi)存占用問題。因?yàn)镃opyOnWrite的寫時(shí)復(fù)制機(jī)制枉层,所以在進(jìn)行寫操作的時(shí)候泉褐,內(nèi)存里會(huì)同時(shí)駐扎兩個(gè)對(duì)象的內(nèi)存,舊的對(duì)象和新寫入的對(duì)象
(注意:在復(fù)制的時(shí)候只是復(fù)制容器里的引用鸟蜡,只是在寫的時(shí)候會(huì)創(chuàng)建新對(duì)象添加到新容器里膜赃,而舊容器的對(duì)象還在使用,所以有兩份對(duì)象內(nèi)存)矩欠。 - 數(shù)據(jù)一致性問題财剖。CopyOnWrite容器只能保證數(shù)據(jù)的最終一致性,不能保證數(shù)據(jù)的實(shí)時(shí)一致性癌淮。所以如果若希望寫入的的數(shù)據(jù)躺坟,馬上能讀到,不建議使用CopyOnWrite容器乳蓄。
- 內(nèi)存占用問題。因?yàn)镃opyOnWrite的寫時(shí)復(fù)制機(jī)制枉层,所以在進(jìn)行寫操作的時(shí)候泉褐,內(nèi)存里會(huì)同時(shí)駐扎兩個(gè)對(duì)象的內(nèi)存,舊的對(duì)象和新寫入的對(duì)象
- java1.8 下CopyOnWriteArrayList 和 Collections.synchronizedList(new ArrayList<String>()) 性能測(cè)試結(jié)論:
- 1.6下 CopyOnWriteArrayList 優(yōu)于 synchronizedList網(wǎng)上結(jié)論;
- 1.8下基本差不多,可能是jvm對(duì)synchronized 不斷的優(yōu)化
2.2.3.3Concurrent*集合
均為安全的集合ConcurrentHashMap采用加鎖咪橙,其他幾個(gè)采用CAS無鎖更新
- ConcurrentHashMap
- 1.代碼體積約6000行
- 2.java1.8 ConcurrentHashMap 和 HashMap 結(jié)構(gòu)一樣 ,在put操作時(shí)虚倒,當(dāng)數(shù)組索引位置大于1時(shí)進(jìn)行加鎖操作
- 3.size()(或 mappingCount()) 使用無鎖操作美侦,返回并非準(zhǔn)確值, 因?yàn)榇藭r(shí)可能有線程對(duì)集合進(jìn)行刪除或增加操作
- 4.比較:
- java1.7
- 1.版本采用Segment<K,V>[] segments數(shù)組,Segment繼承ReentrantLock魂奥,實(shí)現(xiàn)鎖分段菠剩,進(jìn)行安全操作
- 2.每個(gè)Segment相當(dāng)于一個(gè)老版本的hashMap(數(shù)據(jù)結(jié)構(gòu)為:table數(shù)組+單向鏈表的數(shù)據(jù)結(jié)構(gòu))
- 3.get時(shí) 取不到值最后在加鎖取一次
- java1.8
- 1.取消segments字段,直接采用 HashEntry<K,V>[] table保存數(shù)據(jù)
- 2.數(shù)據(jù)結(jié)構(gòu)改為:變更為table數(shù)組+單向鏈表(或紅黑樹)
- 3.get() 使用無鎖操作耻煤,取不到直接返回空
- java1.7
- 5.1.8中的鎖力度更小具壮,數(shù)據(jù)結(jié)構(gòu)更簡(jiǎn)單
- 6.CounterCell何時(shí)使用 沒有理解透徹?
- ConcurrentLinkedDeque
- 1.代碼體積約1500行
- 2.一個(gè)無界線程安全雙向鏈表
- 3.使用unsafe 保證原子性
- ConcurrentLinkedQueue
- 一個(gè)無界線程安全FIFO隊(duì)列
- ConcurrentSkipListMap
- 是TreeMap的多線的安全版本
- 數(shù)據(jù)結(jié)構(gòu)使用跳表保存數(shù)據(jù)哈蝇,實(shí)質(zhì)為一種鏈表
Key-Value數(shù)據(jù)結(jié)構(gòu)
目前常用的key-value數(shù)據(jù)結(jié)構(gòu)有三種:Hash表棺妓、紅黑樹、SkipList
名稱 | 描述 |
---|---|
hash表 | 即數(shù)組:例如:Hashmap(Hash表+鏈表+紅黑樹)炮赦,HashTbale(Hash表+鏈表) 插入怜跑、查找最快,為O(1)吠勘;如使用鏈表實(shí)現(xiàn)則可實(shí)現(xiàn)無鎖性芬;數(shù)據(jù)有序化需要顯式的排序操作。 |
紅黑樹 | 例如:TreeMap 插入看幼、查找為O(logn)批旺,但常數(shù)項(xiàng)較小诵姜;無鎖實(shí)現(xiàn)的復(fù)雜性很高,一般需要加鎖;數(shù)據(jù)天然有序棚唆。 |
Skip表 | 例如:ConcurrentSkipListMap,插入暇赤、查找為O(logn),但常數(shù)項(xiàng)比紅黑樹要大宵凌;底層結(jié)構(gòu)為鏈表鞋囊,可無鎖實(shí)現(xiàn);數(shù)據(jù)天然有序瞎惫。 |
2.2.4線程池
線程池常用類關(guān)系
2.2.4.1ExecutorCompletionService
- CompletionService實(shí)現(xiàn)了生產(chǎn)者提交任務(wù)和消費(fèi)者獲取結(jié)果的解耦溜腐,消費(fèi)者一定是按照任務(wù)完成的先后順序來獲取執(zhí)行結(jié)果,注意不是提交順序。(自定義的實(shí)現(xiàn)為按照提交順序)
- 基本實(shí)現(xiàn):Executor+阻塞隊(duì)列實(shí)現(xiàn)
- 一組任務(wù)獲取結(jié)果的場(chǎng)景
2.2.4.2FutureTask
- 僅在計(jì)算完成時(shí)才能獲取結(jié)果瓜喇;如果計(jì)算尚未完成挺益,則阻塞 get 方法。
- 單個(gè)任務(wù)獲取結(jié)果的場(chǎng)景
- 可使用 FutureTask 包裝 Callable 或 Runnable 對(duì)象乘寒。因?yàn)?FutureTask 實(shí)現(xiàn)了 Runnable望众,所以可將 FutureTask 提交給 Executor 執(zhí)行。
- FutureTask和Callable的關(guān)系伞辛?
- 由于線程池(AbstractExecutorService)只執(zhí)行Callable類型的任務(wù) ,提交的 Runnable 也會(huì)轉(zhuǎn)化為Callable烂翰。
- 從類型上講:FutureTask = Runnable+Future ;FutureTask內(nèi)部又維護(hù)一個(gè)Callable,所以實(shí)際上又是一個(gè) Callable
- 總之:FutureTask既是Runnable 又是Callable 蚤氏,還有Future的特性甘耿。
2.2.4.3兩種線程池
- ThreadPoolExecutor
- ThreadPoolExecutor調(diào)節(jié)線程的原則是:先調(diào)整到最小線程,最小線程用完后竿滨,它會(huì)優(yōu)先將任務(wù) 放入緩存隊(duì)列(offer(task)),等緩沖隊(duì)列用完了佳恬,才會(huì)向最大線程數(shù)調(diào)節(jié)。
- 線程池線程參數(shù)設(shè)置原則
- 無界隊(duì)列:大小線程數(shù)建議設(shè)置成一致姐呐,如果使用無界隊(duì)列 殿怜,就不可能使用最大線程數(shù)
- 有界隊(duì)列:大小線程數(shù)可以不一致(當(dāng)有界隊(duì)列特別大時(shí),也沒有必要把core和max設(shè)置的不一樣曙砂, 因?yàn)榭赡芎茈y達(dá)到有界的最值头谜,也就更難達(dá)到max的值了)
- 繼承關(guān)系:
Executor->ExecutorService->AbstractExecutorService->ThreadPoolExecutor - 理解線程復(fù)用?(難點(diǎn))
- ScheduledThreadPoolExecutor
- ScheduledExecutorService的實(shí)現(xiàn)類ScheduledThreadPoolExecutor是繼承線程池類ThreadPoolExecutor的鸠澈,因此它擁有線程池的全部特性柱告。但是同時(shí)又是一種特殊的線程池,這個(gè)
線程池的線程數(shù)大小最大Integer最大值笑陈,任務(wù)隊(duì)列是基于DelayQueue的無限任務(wù)隊(duì)列际度。
主要提供定時(shí)執(zhí)行功能,通過延時(shí)隊(duì)列實(shí)現(xiàn) - 繼承關(guān)系:
Executor->ExecutorService->AbstractExecutorService->ThreadPoolExecutor->ScheduledExecutorService - scheduleAtFixedRate【不關(guān)注上次線程執(zhí)行完成】和scheduleWithFixedDelay【關(guān)注上次線程執(zhí)行完成】
- 功能效果和Timer類似
2.2.4.4拒絕策略
- 拒絕策略4個(gè)類屬于ThreadPoolExecutor的內(nèi)部類
- 具體使用區(qū)別
- AbortPolicy:當(dāng)提交的不能立即執(zhí)行且阻塞隊(duì)列無法容納時(shí)涵妥, 拋出異常乖菱,后續(xù)生產(chǎn)線程不能再正常提交到線程池
- CallerRunsPolicy:當(dāng)提交的不能立即執(zhí)行且阻塞隊(duì)列無法容納時(shí),則立即執(zhí)行,后續(xù)正常提交到線程池
- DiscardOldestPolicy:當(dāng)提交的不能立即執(zhí)行且阻塞隊(duì)列無法容納時(shí),移除調(diào)隊(duì)列中最早的任務(wù),后續(xù)正常提交到線程池
- DiscardPolicy:當(dāng)提交的不能立即執(zhí)行且阻塞隊(duì)列無法容納時(shí)窒所,丟棄提交的任務(wù),后續(xù)正常提交到線程池
2.2.5框架
- Executors
- 也可以認(rèn)為是創(chuàng)建線程池的一個(gè)工具類
- 提供創(chuàng)建線程池的一些靜態(tài)方法
- Fork/Join
- Fork/Join實(shí)現(xiàn)了“工作竊取算法”鹉勒,F(xiàn)orkJoinTask需要通過ForkJoinPool來執(zhí)行,任務(wù)分割出的子任務(wù)會(huì)添加到當(dāng)前工作線程所維護(hù)的雙端隊(duì)列中吵取,進(jìn)入隊(duì)列的頭部禽额。當(dāng)一個(gè)工作線程的隊(duì)列里暫時(shí)沒有任務(wù)時(shí),它會(huì)隨機(jī)從其他工作線程的隊(duì)列的尾部獲取一個(gè)任務(wù)皮官。
- Fork/Join框架的設(shè)計(jì)主要2步任務(wù)
- 第一步分割任務(wù)脯倒。首先我們需要有一個(gè)fork類來把大任務(wù)分割成子任務(wù),有可能子任務(wù)還是很大捺氢,所以還需要不停的分割藻丢,直到分割出的子任務(wù)足夠小。
- 第二步執(zhí)行任務(wù)并合并結(jié)果讯沈。分割的子任務(wù)分別放在雙端隊(duì)列里郁岩,然后幾個(gè)啟動(dòng)線程分別從雙端隊(duì)列里獲取任務(wù)執(zhí)行。子任務(wù)執(zhí)行完的結(jié)果都統(tǒng)一放在一個(gè)隊(duì)列里缺狠,啟動(dòng)一個(gè)線程從隊(duì)列里拿數(shù)據(jù)问慎,然后合并這些數(shù)據(jù)。
- 應(yīng)用
- 第一步:創(chuàng)建任務(wù)job挤茄,繼承ForkJoinTask的子類:
-----RecursiveAction:用于沒有返回結(jié)果的任務(wù)如叼。
-----RecursiveTask :用于有返回結(jié)果的任務(wù)。 - 第二步:提交job到ForkJoinPool(獲取結(jié)果如果有)
- 第一步:創(chuàng)建任務(wù)job挤茄,繼承ForkJoinTask的子類:
2.2.6工具類
- CountDownLatch : 基于AQS共享鎖的實(shí)現(xiàn)穷劈,當(dāng)state為0時(shí)喚醒等待的一個(gè)或一組線程
- CyclicBarrier : 內(nèi)部維護(hù)一個(gè)count 當(dāng)count為0時(shí)笼恰,等待的線程開始運(yùn)行,恢復(fù)CyclicBarrier的count為原初始值parties歇终,所以相比CountDownLatch是可以重復(fù)使用的
- Exchanger:線程互換數(shù)據(jù)
- 每個(gè)線程既可以充當(dāng)消費(fèi)者社证,也可以充當(dāng)生產(chǎn)者
- 當(dāng)生產(chǎn)大于消費(fèi)時(shí),如果一直沒有被消費(fèi)完评凝,線程將處于阻塞狀態(tài)
- Semaphore:
- 基于AQS共享鎖的實(shí)現(xiàn)
- Semaphore可以應(yīng)用于流量控制追葡,比如對(duì)數(shù)據(jù)庫(kù)的連接數(shù)控制
2.2.7常見問題(整理自并發(fā)編程網(wǎng))
2.2.8 jvm常用命令
jps、jstack奕短、jmap宜肉、jhat、jstat