java源碼學(xué)習(xí),(基于java version 1.8.0_60)
1.基礎(chǔ)部分(原理,運(yùn)用熟練掌握)
1.1java.util
這個(gè)包主要以集合為主,這是數(shù)據(jù)結(jié)構(gòu)的最佳實(shí)踐。
集合羅列
| 集合 | 查詢(xún)時(shí)間復(fù)雜度 | 繼承前列 | 線程安全 | 支持排序 |
|---|---|---|---|---|
| Map | HashMap | LinkedHashMap | HashTable | TreeMap |
| Set | HashSet | LinkedHashSet | TreeSet | |
| ~ | 無(wú) | 無(wú) | 線程安全 | 繼承前列線程安全 |
| List | ArrayList | LinkedList | Vector | Stack |
相似比較
| Name | 同作用 | 不同作用 |
|---|---|---|
| Iterator | 支持遍歷 | 支持fail-fast[ConcurrentModificationException]機(jī)制,通過(guò)迭代器可以對(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ù)雜的類(lèi),搞清楚map的常用實(shí)現(xiàn)類(lèi)是非常關(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ù))+ 單向鏈表+紅黑樹(shù)(又叫自平衡二叉查找樹(shù))。
- Node實(shí)體基本結(jié)構(gòu):hash,key,value,next(鏈表的下一個(gè)實(shí)體);Map.Entry<K, V> 實(shí)現(xiàn)類(lèi)。
- TreeNode實(shí)體基本結(jié)構(gòu):parent,left,right,prev;TreeNode繼承LinkedHashMap.Entry繼承HashMap.Node<K,V>。
- 數(shù)組上的元素為鏈表(或紅黑樹(shù))的頭節(jié)點(diǎn)(或根節(jié)點(diǎn))。
- 對(duì)于hash沖突解決辦法使用鏈表(或紅黑樹(shù)),當(dāng)鏈表長(zhǎng)度大于等于8時(shí)轉(zhuǎn)化為紅黑樹(shù)。之所以轉(zhuǎn)化為紅黑樹(shù)是當(dāng)數(shù)量大時(shí)紅黑樹(shù)的查詢(xún)效率更高。
- 操作紅黑樹(shù),增加刪除后為了自平衡 進(jìn)行的左右旋和重新著色。
幾種時(shí)間復(fù)雜度
| 結(jié)構(gòu) | 查詢(xún)時(shí)間復(fù)雜度 | 說(shuō)明 |
|---|---|---|
| hash表 | O(1) | |
| 鏈表 | O(n) | 遍歷鏈表去查找 |
| 紅黑樹(shù) | O(log n) |
多線程下當(dāng)共享變量使用時(shí),擴(kuò)容鏈表造成的問(wèn)題
| 版本 | 元素是否倒置 | 元素是否丟失 | 復(fù)雜程度 |
|---|---|---|---|
| 1.7 | 倒置(易形成閉合回路) | 沒(méi)驗(yàn)證 | 代碼量約1000行 |
| 1.8 | 不倒置(99%的認(rèn)為不會(huì)形成,1%再驗(yàn)證) | 會(huì)丟失 | 代碼量約2000行 |
| 解決辦法 | 不當(dāng)共享變量使用(在局部變量中 new創(chuàng)建 是沒(méi)問(wèn)題的,在棧中操作,屬于私有的)或使用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)類(lèi)。
- 擴(kuò)容時(shí)元素順序不變。
- 由于HashTable是同步的,多數(shù)方法添加了synchronized關(guān)鍵字同步。
1.1.1.3TreeMap
- 數(shù)據(jù)結(jié)構(gòu):紅黑樹(shù)(天然排序)。
- Entry結(jié)構(gòu):key,value, left,right,parent,color
- 由于這是排序的hash表,必須在構(gòu)造函數(shù)傳遞比較器Comparator實(shí)現(xiàn)類(lèi)或添加的實(shí)體實(shí)現(xiàn)Comparable接口
- Comparator和Comparable區(qū)別:Comparable,方便,實(shí)體直接繼承,不靈活。Comparator,靈活,根據(jù)業(yè)務(wù)多維度實(shí)現(xiàn)比較
- 實(shí)現(xiàn)結(jié)果基本和HashMap的紅黑樹(shù)實(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)開(kāi)始,保證了輸入的順序和添加順序一致
幾種map總結(jié):
1、在存儲(chǔ)上基本與value沒(méi)有關(guān)系,主要依賴(lài)K和K的hash值來(lái)放置到合適位置。
2、內(nèi)部實(shí)現(xiàn)上基本以循環(huán)操作為主。
1.1.2Set相關(guān)
1.1.2.1HashSet
- 實(shí)現(xiàn)依賴(lài)于HashMap
1.1.2.3LinkedHashSet
- 實(shí)現(xiàn)依賴(lài)于LinkedHashMap(通過(guò)構(gòu)造函數(shù)調(diào)用HashSet的LinkedHashMap構(gòu)造函數(shù)實(shí)現(xiàn))
1.1.2.2TreeSet
- 支持排序,間接實(shí)現(xiàn)SortedSet接口
- 實(shí)現(xiàn)依賴(lài)于NavigableMap
1.1.3List相關(guān)
1.1.2.1ArrayList
- 數(shù)據(jù)結(jié)構(gòu):數(shù)組
- 擴(kuò)容依賴(lài)Arrays.copyOf(),它又依賴(lài)于 System.arraycopy()
- 通過(guò)索引訪問(wèn)遍歷效率最高,而使用迭代器的效率最低(還可以通過(guò)增強(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工具類(lèi)
- Arrays:提供一系列靜態(tài)方法,對(duì)數(shù)組排序,搜索等
- Collections :提供一系列靜態(tài)方法,對(duì)集合 排序,搜索,包裝集合為安全類(lèi)型等。包裝基本實(shí)現(xiàn)思路為,傳入之后賦值給局部變量,通過(guò)synchronized關(guān)鍵字同步對(duì)于傳入集合的原有方法加以限制。
1.2java.io
理解類(lèi)提供的功能,才能更準(zhǔn)確的使用。要理解io必須先了解一些基本的概念和區(qū)別。
- 裝飾模式和適配器模式
- 字符流和字節(jié)流
- 流的基本介質(zhì)和區(qū)別
io字節(jié)輸入流(不是所有的輸入輸出都一一對(duì)應(yīng))

io字節(jié)輸入流
io字節(jié)輸出流

io字節(jié)輸出流
io字符輸入流

io字符輸出流
io字符輸出流

io字符輸出流
1.3java.lang
java的base包,唯一一個(gè)只使用不用導(dǎo)包的包。
主要類(lèi)的類(lèi)型有:基本類(lèi)型的包裝類(lèi)型,異常類(lèi),進(jìn)程類(lèi),Thread的相關(guān)類(lèi),字符(串)的系列類(lèi)。
- Object:主要提供hashcode,equals ,getClass,以及線程之間通信的方式:等待和喚醒。
- System
- 主要是native方法
- 集成一些其他類(lèi)像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除過(guò)集合的其他類(lèi)
- Formatter:公式 %[argument_index$][flags][width][.precision]conversion
2.高級(jí)部分(深刻理解)
2.1java.lang.reflect
2.1.1反射

反射類(lèi)的主要關(guān)系
- 獲取對(duì)象字節(jié)對(duì)象的3種方式
- this.getClass()
- 對(duì)象.class
- Class.forName() 獲取對(duì)象字節(jié)碼對(duì)象
- Class 對(duì)象可以獲取字段數(shù)組,方法數(shù)組,構(gòu)造函數(shù)(通過(guò)方法可以創(chuàng)建實(shí)例),所涉及的權(quán)限修飾符,參數(shù),參數(shù)類(lèi)型,方法的返回值類(lèi)等一切基本都可以獲取到。
2.1.2動(dòng)態(tài)代理

動(dòng)態(tài)代理執(zhí)行邏輯
- 代理模式的應(yīng)用
- 創(chuàng)建過(guò)程類(lèi)似于“高級(jí)裝飾者模式”+“高級(jí)繼承(非繼承實(shí)現(xiàn)類(lèi),繼承的是接口)”
- 生成代理類(lèi)過(guò)程:生成代理對(duì)象的字節(jié)碼數(shù)組->生成字節(jié)碼對(duì)象->通過(guò)構(gòu)造函數(shù)創(chuàng)建實(shí)例
- 代理對(duì)象和被代理對(duì)象的區(qū)別:代理對(duì)象由于是Proxy的子類(lèi),所以持有起橋梁作用的InvocationHandler子類(lèi)(簡(jiǎn)稱(chēng)h),h又持有被代理對(duì),通過(guò)代理對(duì)象給h傳入Method對(duì)象和參數(shù)在加上h持有的被代理對(duì)象就可以對(duì)代理對(duì)象通過(guò)Method的invoke執(zhí)行。其實(shí)就是代理對(duì)象持有自己實(shí)現(xiàn)的InvocationHandler對(duì)象,而被代理對(duì)象是沒(méi)有的。(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ì)象也沒(méi)有什么實(shí)際作用)
- 代理對(duì)象里面的基本方法有equals,toString,hashCode和接口的方法。所有方法里面沒(méi)有額外的邏輯,都是通過(guò)h.invoke(this, method, 參數(shù))去調(diào)用真是對(duì)象的。通過(guò) ProxyGenerator.generateProxyClass("$Proxy11", 接口字節(jié)碼數(shù)組); 可以寫(xiě)入生成代理對(duì)象到文件進(jìn)行查看。
- h對(duì)象里面的invoke是對(duì)被代理對(duì)象的所有方法的代理,想要定制化代理可以在里面通過(guò)傳入的Method對(duì)象獲取方法名進(jìn)行判斷處理。
2.2java.util.concurrent.*
并發(fā)包主要內(nèi)容:原子類(lèi),鎖,容器,線程池,框架,工具類(lèi)

并發(fā)主要內(nèi)容
2.2.1原子類(lèi)
- 關(guān)于Unsafe的并發(fā)性。compareAndSwap*方法是原子的,并且可用來(lái)實(shí)現(xiàn)高性能的、無(wú)鎖(free-lock)的數(shù)據(jù)結(jié)構(gòu)。
可能存在ABA問(wèn)題、指令重排序等問(wèn)題 - 獲取Unsafe實(shí)例的2種方法:反射獲取字段的方法或反射獲取構(gòu)造函數(shù)獲取
- 自定義實(shí)現(xiàn)Integer的原子操作類(lèi):CAtomicInteger
- 原子類(lèi)主要提供:3類(lèi)(int,long,Reference),每類(lèi)3種(本身,數(shù)組,字段)的更新
外加Boolean(底層轉(zhuǎn)化為int),具體實(shí)現(xiàn)對(duì)應(yīng)于Unsafe類(lèi)的方法 - unsafe.compareAndSwapInt(arg0, arg1, arg2, arg3)
- unsafe.compareAndSwapLong(arg0, arg1, arg2, arg3)
- unsafe.compareAndSwapObject(arg0, arg1, arg2, arg3)
數(shù)組的原子更新是通過(guò)索引號(hào) 更新的具體數(shù)值的
若想要實(shí)現(xiàn)其他基本類(lèi)型,可以通過(guò)轉(zhuǎn)化為int 或long類(lèi)型轉(zhuǎn)化去操作 - 實(shí)現(xiàn)原理大致過(guò)程如下:
- 有一些狀態(tài)
- 創(chuàng)建它的副本
- 修改它
- 執(zhí)行CAS
- 如果失敗,重復(fù)嘗試直到成功
無(wú)鎖編程(lock free)
常見(jiàn)的lock free編程一般是基于CAS(Compare And Swap)操作:CAS(void ptr, Any oldValue, Any newValue);即查看內(nèi)存地址ptr處的值,如果為oldValue則將其改為 newValue,并返回true,否則返回false。 X86平臺(tái)上的CAS操作一般是通過(guò)CPU的CMPXCHG指令來(lái)完成的。CPU在執(zhí)行此指令時(shí)會(huì)首先鎖住CPU總線,禁止其它核心對(duì)內(nèi)存的訪問(wèn),然后再查看或修改ptr的值。簡(jiǎn)單的說(shuō)CAS利用了CPU的硬件鎖來(lái)實(shí)現(xiàn)對(duì)共享資源的串行使用。
優(yōu)點(diǎn):
- a、開(kāi)銷(xiāo)較小:不需要進(jìn)入內(nèi)核,不需要切換線程;
- b、沒(méi)有死鎖:總線鎖最長(zhǎng)持續(xù)為一次read+write的時(shí)間;
- c、只有寫(xiě)操作需要使用CAS,讀操作與串行代碼完全相同,可實(shí)現(xiàn)讀寫(xiě)不互斥。
缺點(diǎn):
- a、編程非常復(fù)雜,兩行代碼之間可能發(fā)生任何事,很多常識(shí)性的假設(shè)都不成立。
- b、CAS模型覆蓋的情況非常少,無(wú)法用CAS實(shí)現(xiàn)原子的復(fù)數(shù)操作。
2.2.2鎖
主要類(lèi)關(guān)系:

Lock包主要類(lèi)關(guān)系
2.2.2.1AQS
- 鎖是面向用戶(hù)的,同步器是面向鎖的(就是鎖的內(nèi)部實(shí)現(xiàn)),AQS支持互斥鎖,共享鎖的實(shí)現(xiàn)。
- AQS實(shí)現(xiàn)一個(gè)FIFO等待隊(duì)列。
- 通過(guò)對(duì)state的原子修改來(lái)實(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:用于阻塞或者喚醒線程,屬于工具類(lèi),不和鎖關(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說(shuō)明)
- 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說(shuō)明)
- lock:獲取不到鎖阻塞該線程,加入同步隊(duì)列
- unLock:釋放鎖喚醒后繼節(jié)點(diǎn)
- await:主要干三件事:1.阻塞該線程 2.添加到等待隊(duì)列3. 喚醒后繼線程
- signal:主要干1件事:加入同步隊(duì)列
2.2.2.2ReentrantLock

ReentrantLock內(nèi)部關(guān)系
- 主要理解公平鎖和非公平鎖的在獲取鎖時(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ì)列
- 性能問(wèn)題:(TReentrantLock2測(cè)試)
- 前提:把不同線程獲取鎖的一次定義為一次上下文切換
- 獲取同等鎖次數(shù)情況下,非公平鎖相對(duì)用時(shí)更少,原因是減少了cpu的上下文切換
- 公平鎖執(zhí)行較多上下文切換次數(shù), 而非公平鎖執(zhí)行上下文切換次數(shù)較少(原因是當(dāng)一個(gè)線程獲取釋放鎖后,下一次如果它再需要鎖,相對(duì)比其他線程獲取鎖的概率更大,此時(shí)就不需要切換就相對(duì)省時(shí))
2.2.2.3ReentrantReadWriteLock

ReentrantReadWriteLock內(nèi)部關(guān)系
- 出現(xiàn)讀寫(xiě)鎖的緣由:當(dāng)多讀少寫(xiě)時(shí),使用讀寫(xiě)鎖比使用互斥鎖具有更高的并發(fā)性
- 特點(diǎn):(讀鎖可以并發(fā)訪問(wèn),寫(xiě)鎖時(shí)其他的讀鎖和其他的寫(xiě)鎖都被阻塞)
- 主要依據(jù)32位的int類(lèi)型的state的低16位的值表示寫(xiě)鎖(為互斥鎖);高16位的值表示讀鎖(為共享鎖)
- 通過(guò)位運(yùn)算把state值拆分為2個(gè)值:假設(shè)當(dāng)前狀態(tài)為s
- 寫(xiě)狀態(tài):s & (1 << 16) - 1 即:s&65535,也就是s與16個(gè)1(2進(jìn)制的),這樣就把高16位抹去了;當(dāng)寫(xiě)狀態(tài)+1時(shí),即為s+1,只給低位加。
- 讀狀態(tài):s>>>16 ,表示無(wú)符號(hào)補(bǔ)0右移16位;當(dāng)讀狀態(tài)+1時(shí),等于s+(1<<16),結(jié)果就是只給高位加
- (鎖降級(jí))流程:先獲取寫(xiě)鎖,在獲取讀鎖,在釋放寫(xiě)鎖,在釋放讀鎖(不支持鎖升級(jí))
2.2.3容器
主要并發(fā)容器

并發(fā)容器
2.2.3.1隊(duì)列
- 阻塞隊(duì)列主要方法說(shuō)明:
- [add:remove]沒(méi)有值或隊(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開(kāi)始 。
- while和if的選擇?
- 必須使用while,因?yàn)樵趕ignal()take1線程時(shí),只會(huì)從阻塞隊(duì)列轉(zhuǎn)移到同步隊(duì)列(不見(jiàn)得會(huì)獲取鎖真正喚醒), 此時(shí)可能已經(jīng)有其他take2線程獲取到了鎖,把隊(duì)列中僅有的元素移除了,此時(shí)take2線程執(zhí)行完后是釋放鎖,喚醒后 繼節(jié)點(diǎn)也就是take1線程。此時(shí)take1線程應(yīng)該再次判斷,條件不滿足繼續(xù)阻塞。
- 能否通過(guò)公平鎖或不公平鎖避免這個(gè)問(wèn)題? 不能,因?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í)行;不符合跳過(guò),while:多使用于多線程喚醒后的再次檢查條件
- signal和signalAll:使用這倆個(gè)都可以,只不過(guò)只會(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è)無(wú)界阻塞隊(duì)列,雖然此隊(duì)列邏輯上是無(wú)界的,但是資源被耗盡時(shí)試圖執(zhí)行 add 操作也將失敗,導(dǎo)致 OutOfMemoryError。
不支持先進(jìn)先出原則 - 由于隊(duì)列是無(wú)界的,所以put方法不會(huì)由于滿了(始終不會(huì)滿)而導(dǎo)致阻塞 。
- 不保證具有同等優(yōu)先級(jí)的元素的順序
- 入隊(duì)(通過(guò)比較找到合適位置入隊(duì))相同(值相等)元素不會(huì)覆蓋,出對(duì)從隊(duì)列頭出隊(duì)
- DelayQueue:一個(gè)無(wú)界阻塞隊(duì)列,內(nèi)部根據(jù)PriorityQueue(無(wú)界線程不安全隊(duì)列) 存儲(chǔ)元素,適用場(chǎng)景:定時(shí)執(zhí)行(獲?。?,緩存失效等
- LinkedBlockingDeque:鏈表實(shí)現(xiàn)的有界阻塞雙端隊(duì)列,支持同端存取(FILO)和異端存?。‵IFO)。如果未指定容量,那么容量將等于 Integer.MAX_VALUE
-
LinkedTransferQueue:一個(gè)鏈表實(shí)現(xiàn)的無(wú)界阻塞隊(duì)列
- tryTransfer()嘗試傳遞給消費(fèi)者 ,沒(méi)有消費(fèi)者放入隊(duì)列
- transfer()嘗試傳遞給消費(fèi)者 ,沒(méi)有消費(fèi)者自己處于阻塞狀態(tài),這種模式類(lèi)似與SynchronousQueue
- 無(wú)論是transfer還是tryTransfer方法,在>=1個(gè)消費(fèi)者線程等待獲取元素時(shí)(此時(shí)隊(duì)列為空),都會(huì)立刻轉(zhuǎn)交,這屬于線程之間的元素交換。注意,這時(shí),元素并沒(méi)有進(jìn)入隊(duì)列。
- 在隊(duì)列中已有數(shù)據(jù)情況下,transfer將需要等待前面數(shù)據(jù)被消費(fèi)掉,直到傳遞的元素e被消費(fèi)線程取走為止。
-
SynchronousQueue:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列
- 其中每個(gè)插入操作必須等待另一個(gè)線程的對(duì)應(yīng)移除操作,如果沒(méi)被消費(fèi)則一直處于阻塞
- 此同步隊(duì)列沒(méi)有任何內(nèi)部容量,甚至連一個(gè)隊(duì)列的容量都沒(méi)有,適合傳遞性的應(yīng)用場(chǎng)景
2.2.3.2Copy-On-Write簡(jiǎn)稱(chēng)COW,僅2個(gè)類(lèi)Set和List
- Copy-On-Write簡(jiǎn)稱(chēng)COW,是一種用于程序設(shè)計(jì)中的優(yōu)化策略,采用數(shù)組存儲(chǔ)。
- 使用場(chǎng)景:多讀少寫(xiě)(讀不加鎖,寫(xiě)會(huì)加鎖,防止多個(gè)線程多多個(gè)拷貝同時(shí)造成數(shù)據(jù)混亂,所以會(huì)加鎖防止此問(wèn)題)
- 寫(xiě)時(shí)采用復(fù)制新的容器,進(jìn)行修改;新容器為原容器中對(duì)象的引用,使用完把新容器賦值給類(lèi)的原有容器引用。復(fù)制新的容器時(shí),這是一個(gè)比較耗費(fèi)性能的操作
- CopyOnWriteArraySet 采用CopyOnWriteArrayList 作為存儲(chǔ)?;静畈欢?/li>
- CopyOnWrite的缺點(diǎn)
- 內(nèi)存占用問(wèn)題。因?yàn)镃opyOnWrite的寫(xiě)時(shí)復(fù)制機(jī)制,所以在進(jìn)行寫(xiě)操作的時(shí)候,內(nèi)存里會(huì)同時(shí)駐扎兩個(gè)對(duì)象的內(nèi)存,舊的對(duì)象和新寫(xiě)入的對(duì)象
(注意:在復(fù)制的時(shí)候只是復(fù)制容器里的引用,只是在寫(xiě)的時(shí)候會(huì)創(chuàng)建新對(duì)象添加到新容器里,而舊容器的對(duì)象還在使用,所以有兩份對(duì)象內(nèi)存)。 - 數(shù)據(jù)一致性問(wèn)題。CopyOnWrite容器只能保證數(shù)據(jù)的最終一致性,不能保證數(shù)據(jù)的實(shí)時(shí)一致性。所以如果若希望寫(xiě)入的的數(shù)據(jù),馬上能讀到,不建議使用CopyOnWrite容器。
- 內(nèi)存占用問(wèn)題。因?yàn)镃opyOnWrite的寫(xiě)時(shí)復(fù)制機(jī)制,所以在進(jìn)行寫(xiě)操作的時(shí)候,內(nèi)存里會(huì)同時(shí)駐扎兩個(gè)對(duì)象的內(nèi)存,舊的對(duì)象和新寫(xiě)入的對(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無(wú)鎖更新
- ConcurrentHashMap
- 1.代碼體積約6000行
- 2.java1.8 ConcurrentHashMap 和 HashMap 結(jié)構(gòu)一樣 ,在put操作時(shí),當(dāng)數(shù)組索引位置大于1時(shí)進(jìn)行加鎖操作
- 3.size()(或 mappingCount()) 使用無(wú)鎖操作,返回并非準(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ù)組+單向鏈表(或紅黑樹(shù))
- 3.get() 使用無(wú)鎖操作,取不到直接返回空
- java1.7
- 5.1.8中的鎖力度更小,數(shù)據(jù)結(jié)構(gòu)更簡(jiǎn)單
- 6.CounterCell何時(shí)使用 沒(méi)有理解透徹?
- ConcurrentLinkedDeque
- 1.代碼體積約1500行
- 2.一個(gè)無(wú)界線程安全雙向鏈表
- 3.使用unsafe 保證原子性
- ConcurrentLinkedQueue
- 一個(gè)無(wú)界線程安全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表、紅黑樹(shù)、SkipList
| 名稱(chēng) | 描述 |
|---|---|
| hash表 | 即數(shù)組:例如:Hashmap(Hash表+鏈表+紅黑樹(shù)),HashTbale(Hash表+鏈表) 插入、查找最快,為O(1);如使用鏈表實(shí)現(xiàn)則可實(shí)現(xiàn)無(wú)鎖;數(shù)據(jù)有序化需要顯式的排序操作。 |
| 紅黑樹(shù) | 例如:TreeMap 插入、查找為O(logn),但常數(shù)項(xiàng)較小;無(wú)鎖實(shí)現(xiàn)的復(fù)雜性很高,一般需要加鎖;數(shù)據(jù)天然有序。 |
| Skip表 | 例如:ConcurrentSkipListMap,插入、查找為O(logn),但常數(shù)項(xiàng)比紅黑樹(shù)要大;底層結(jié)構(gòu)為鏈表,可無(wú)鎖實(shí)現(xiàn);數(shù)據(jù)天然有序。 |
2.2.4線程池
線程池常用類(lèi)關(guān)系

線程池常用類(lèi)關(guān)系
2.2.4.1ExecutorCompletionService
- CompletionService實(shí)現(xiàn)了生產(chǎn)者提交任務(wù)和消費(fèi)者獲取結(jié)果的解耦,消費(fèi)者一定是按照任務(wù)完成的先后順序來(lái)獲取執(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類(lèi)型的任務(wù) ,提交的 Runnable 也會(huì)轉(zhuǎn)化為Callable。
- 從類(lèi)型上講: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è)置原則
- 無(wú)界隊(duì)列:大小線程數(shù)建議設(shè)置成一致,如果使用無(wú)界隊(duì)列 ,就不可能使用最大線程數(shù)
- 有界隊(duì)列:大小線程數(shù)可以不一致(當(dāng)有界隊(duì)列特別大時(shí),也沒(méi)有必要把core和max設(shè)置的不一樣, 因?yàn)榭赡芎茈y達(dá)到有界的最值,也就更難達(dá)到max的值了)
- 繼承關(guān)系:
Executor->ExecutorService->AbstractExecutorService->ThreadPoolExecutor - 理解線程復(fù)用?(難點(diǎn))
- ScheduledThreadPoolExecutor
- ScheduledExecutorService的實(shí)現(xiàn)類(lèi)ScheduledThreadPoolExecutor是繼承線程池類(lèi)ThreadPoolExecutor的,因此它擁有線程池的全部特性。但是同時(shí)又是一種特殊的線程池,這個(gè)
線程池的線程數(shù)大小最大Integer最大值,任務(wù)隊(duì)列是基于DelayQueue的無(wú)限任務(wù)隊(duì)列。
主要提供定時(shí)執(zhí)行功能,通過(guò)延時(shí)隊(duì)列實(shí)現(xiàn) - 繼承關(guān)系:
Executor->ExecutorService->AbstractExecutorService->ThreadPoolExecutor->ScheduledExecutorService - scheduleAtFixedRate【不關(guān)注上次線程執(zhí)行完成】和scheduleWithFixedDelay【關(guān)注上次線程執(zhí)行完成】
- 功能效果和Timer類(lèi)似
2.2.4.4拒絕策略
- 拒絕策略4個(gè)類(lèi)屬于ThreadPoolExecutor的內(nèi)部類(lèi)
- 具體使用區(qū)別
- AbortPolicy:當(dāng)提交的不能立即執(zhí)行且阻塞隊(duì)列無(wú)法容納時(shí), 拋出異常,后續(xù)生產(chǎn)線程不能再正常提交到線程池
- CallerRunsPolicy:當(dāng)提交的不能立即執(zhí)行且阻塞隊(duì)列無(wú)法容納時(shí),則立即執(zhí)行,后續(xù)正常提交到線程池
- DiscardOldestPolicy:當(dāng)提交的不能立即執(zhí)行且阻塞隊(duì)列無(wú)法容納時(shí),移除調(diào)隊(duì)列中最早的任務(wù),后續(xù)正常提交到線程池
- DiscardPolicy:當(dāng)提交的不能立即執(zhí)行且阻塞隊(duì)列無(wú)法容納時(shí),丟棄提交的任務(wù),后續(xù)正常提交到線程池
2.2.5框架
- Executors
- 也可以認(rèn)為是創(chuàng)建線程池的一個(gè)工具類(lèi)
- 提供創(chuàng)建線程池的一些靜態(tài)方法
- Fork/Join
- Fork/Join實(shí)現(xiàn)了“工作竊取算法”,F(xiàn)orkJoinTask需要通過(guò)ForkJoinPool來(lái)執(zhí)行,任務(wù)分割出的子任務(wù)會(huì)添加到當(dāng)前工作線程所維護(hù)的雙端隊(duì)列中,進(jìn)入隊(duì)列的頭部。當(dāng)一個(gè)工作線程的隊(duì)列里暫時(shí)沒(méi)有任務(wù)時(shí),它會(huì)隨機(jī)從其他工作線程的隊(duì)列的尾部獲取一個(gè)任務(wù)。
- Fork/Join框架的設(shè)計(jì)主要2步任務(wù)
- 第一步分割任務(wù)。首先我們需要有一個(gè)fork類(lèi)來(lái)把大任務(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的子類(lèi):
-----RecursiveAction:用于沒(méi)有返回結(jié)果的任務(wù)。
-----RecursiveTask :用于有返回結(jié)果的任務(wù)。 - 第二步:提交job到ForkJoinPool(獲取結(jié)果如果有)
- 第一步:創(chuàng)建任務(wù)job,繼承ForkJoinTask的子類(lèi):
2.2.6工具類(lèi)
- CountDownLatch : 基于AQS共享鎖的實(shí)現(xiàn),當(dāng)state為0時(shí)喚醒等待的一個(gè)或一組線程
- CyclicBarrier : 內(nèi)部維護(hù)一個(gè)count 當(dāng)count為0時(shí),等待的線程開(kāi)始運(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í),如果一直沒(méi)有被消費(fèi)完,線程將處于阻塞狀態(tài)
- Semaphore:
- 基于AQS共享鎖的實(shí)現(xiàn)
- Semaphore可以應(yīng)用于流量控制,比如對(duì)數(shù)據(jù)庫(kù)的連接數(shù)控制
2.2.7常見(jiàn)問(wèn)題(整理自并發(fā)編程網(wǎng))
2.2.8 jvm常用命令
jps、jstack、jmap、jhat、jstat