1、線程池
過於頻繁的建立/銷燬線程浪費性能,線程併發數量過多, JVM調度是搶佔式的,線程上線文切換搶佔系統資源致使阻塞。
1.線程池線程數:程序員
通常CPU密集型:CPU+1
IO密集型:[(線程等待時間+線程CPU時間)/線程CPU時間]*CPU
2.線程池建立4種方式:
newCachedThreadPool 緩存線程池,沒有線程可用就建立,空閒線程60秒未使用將被回收。
newFixedThreadPool 固定線程數,多餘的任務進隊列,(若須要)異常死後從新啓動一個代替
newScheduledThreadPool 延遲或按期執行的線程池
newSingleThreadExecutor 單線程池,異常死後從新啓動一個代替。
以上4個都是由Executors提供,底層都是由ThreadPoolExecutor實現的。
newFixedThreadPool(1)與newSingleThreadExecutor 主要區別,後者被DelegatedExecutorService裝飾了,減小了不少暴露方法
3.ThreadPoolExecutor 構造參數有5/6/7個。
corePoolSize 核心線程數
maximumPoolSize 最大線程數
keepAliveTime 非核心線程數超時時長
TimeUnit 超時單位
BlockingQueue<Runnable> 線程池的任務對列
ThreadFactory 線程工廠 通常默認便可
RejectedExecutionHandler 拒絕策略
執行規則:
1. 線程數量未達到corePoolSize,則新建一個線程(核心線程)執行任務
2. 線程數量達到了corePools,則將任務移入隊列等待
3. 隊列已滿,新建線程(非核心線程)執行任務
4. 隊列已滿,總線程數又達到了maximumPoolSize,就會拋出拒絕策略異常
5. 當線程池中的線程數量大於 corePoolSize時,若是某線程空閒時間超過keepAliveTime,線程將被終止
經常使用隊列:
ArrayBlockingQueue :由數組結構組成的阻塞隊列,可支持公平與非公平
LinkedBlockingQueue :由鏈表結構組成的阻塞隊列,兩端獨立鎖提供併發
SynchronousQueue:無容量無緩衝的阻塞隊列,對應 newCachedThreadPool
DelayedWorkQueue:延遲阻塞隊列。對應 線程池 newScheduledThreadPool 。
拒絕策略:
AbortPolicy :默認的,丟棄任務並拋出RejectedExecutionException異常
DiscardPolicy:丟棄任務,不拋出異常
DiscardOldestPolicy:丟棄隊列前面任務,從新嘗試執行任務
CallerRunsPolicy:由調用線程處理該任務。
2、線程
1.線程狀態:
新建:使用new對象,JVM僅分配內存與成員變量值
就緒:線程對象調用start()後,JVM建立 方法調用棧與程序計數器,等待CPU調度運行。
運行:線程得到CPU執行了run方法執行體。
阻塞:
死亡:
2.終止線程方式:
退出標誌退出線程: 伺服線程使用volatile 類型標誌設置值 判斷while循環退出。
Interrupt()中斷線程:線程處於阻塞時,拋出異常。非阻塞時 經過isInterrupted()判斷線程的中斷標誌來退出循環
stop終止線程: 線程不安全,釋放線程持有的全部鎖,數據可能呈現不一致性
sleep()方法屬於Thread類,線程不會釋放對象鎖,
wait()方法屬於Object類,線程會放棄對象鎖,進入等待此對象的等待鎖定池。必需要在同步方法或者同步塊中調用,也就是必須已經得到對象鎖。用於多線程之間的通訊。
notify() 方法,喚醒在此對象監視器上等待的單個線程,並不必定喚醒的是當前線程
yield()進入就緒狀態
join() 方法,當前線程阻塞,等待其餘線程終止後,在變成就緒狀態
interrupt()方法並不會中斷一個正在運行的線程。也就是說處於Running狀態的線程並不會由於被中斷而被終止,僅僅改變了內部維護的中斷標識位而已。在線程的run方法內部能夠根據thread.isInterrupted()的值來優雅的終止線程。
ThreadLocal線程本地變量在線程的生命週期內起做用,減小同一個線程內多個函數或者組件之間一些公共變量的傳遞的複雜度。每一個線程建立了數據副本, 底層維護了一個map, key是ThreadLocal實例, Thread類中持有了threadLocalMap集合。map的entry繼承弱引用, 構造函數將key傳入父類構造函數, key爲弱引用(第二次GC時回收), 當線程不結束後, key被回收, 但value會一直存在。但在每次調用get/set/remove方法時, 都會自動清理key爲null的value, 但若不調用以上3個方法, value仍不會被回收, 通常這個問題會存在線程池,線程是一直在不斷的重複運行的,從而也就形成了value可能形成累積的狀況。解決辦法, 手動調用remove方法。數組
多線程間可經過
共享數據變量對象、內部類共享成員變量
來共享數據
volatile變量可見性、禁止指令重排。保證了可見性和有序性,不能保證原子性,相比synchronized 不會阻塞線程
synchronized 經過操做系統Mutex Lock實現的,重量級鎖。用在方法、代碼塊、對象、靜態方法上(類鎖),線程可見性、原子性、有序性都能保證. JDK1.6後進行了不少的優化有適應自旋、鎖消除、鎖粗化、輕量級鎖及偏向鎖等,效率有了本質上的提升.
final 域,編譯器和處理器要遵照兩個重排序規則:緩存
-
JMM保證final變量初始化時的有序性、禁止編譯器和處理器重排序。安全
-
final做爲不可變對象,正確初始化後(沒有this逃逸),可以保障可見性。多線程
3. synchronized和ReentrantLock的區別:
共同點:協調多線程對共享對象的訪問;都是可重入鎖,同一線程屢次獲取鎖;都保持可見性與互斥性;
不一樣點:ReentrantLock顯示的得到、釋放鎖,synchronized隱式得到釋放鎖;ReentrantLock是API級別的,synchronized是JVM級別的;ReentrantLock能夠實現公平鎖; synchronized是同步阻塞,使用的是悲觀併發策略,lock是同步非阻 塞,採用的是樂觀併發策略;synchronized在發生異常時,會自動釋放線程佔有的鎖,所以不會致使死鎖現象發生,Lock 必須在finally塊主動經過unLock()釋放鎖;lock可響應中斷,可輪迴,可判斷是否獲取鎖,更爲靈活。
CAS(Compare And Swap/Set)比較並交換-樂觀鎖機制-鎖自旋:當某個線程進入方法,執行其中的指令時,不會被其餘線程打斷,而別的線程就像自旋鎖同樣,一直等到該方法執行完成,才由JVM從等待隊列中選擇一個另外一個線程進入。經過版本號,解決ABA問題。
AbstractQueuedSynchronize 抽象的隊列 同步器,AQS定義了一套多線程訪問共享資源的同步器框架,具體資源的獲取/釋放方式交由自定義同步器去實現,許多同步類實現都依賴於它ReentrantLock/Semaphore/CountDownLatch。內部維護了一個共享資源和線程等待隊列。定義了兩種資源共享方式:獨佔資源(ReentrantLock)與共享資源(Semaphore/CountDownLatch)。ReentrantReadWriteLock兩種方式都實現了
CountDownLatch 線程計數器:某個線程等待其餘線程執行完後執行,不能重用
CyclicBarrier 迴環柵欄: 通常用於一組線程互相等待至某個狀態,而後這一組線程再同時執行
Semaphore 信號量 :控制同時訪問的線程個數,用於限流
自旋鎖: 持有鎖的線程能在很短期內釋放鎖資源,那麼等待競爭鎖的線程就不須要作內核態和用戶態之間的切換進入阻塞掛起狀態,它們只須要等一等(自旋),等持有鎖的線程釋放鎖後便可當即獲取鎖,這樣就避免用戶線程和內核的切換的消耗。
輕量級鎖:是使用操做系統互斥量來實現的傳統鎖,爲了在線程交替執行同步塊時提升性能,若併發產生鎖膨脹升級爲重量鎖。
偏向鎖 :在某個線程得到鎖以後,消除這個線程鎖重入(CAS)的開銷 ,在只有一個線程執行同步塊時進一步提升性能。
鎖優化方案:減小鎖持有時間、減少鎖粒度(ConcurrentHashMap)、鎖分離(ReadWriteLock)、鎖粗化、鎖消除(時編譯器時,若是發現不可能被共享的對象,則能夠消除這些對象的鎖操做,多數是由於程序員編碼不規範引發)。
死鎖的緣由:
定義: 當多個線程相互等待對方釋放資源時就會發生死鎖。
死鎖產生的條件:
互斥條件、不剝奪條件、請求和保持條件、循環等待條件。
避免死鎖: 加鎖順序(線程按照必定的順序加鎖);加鎖時限設置超時時間;併發
NIO與IO區別
NIO是採用多路複用IO模型,會有一個線程不斷去輪詢多個socket的狀態,只有當socket真正有讀寫事件時,才真正調用實際的IO讀寫操做。NIO基於Channel和Buffer(緩衝區)進行操做,數據老是從通道讀取到緩衝區中,或者從緩衝區寫入到通道中。Selector(選擇區)用於監聽多個通道的事件,單個線程能夠監聽多個數據通道。
IO是面向流Stream的,每次從流中讀取一個或多個字節; NIO是面向緩衝區的,數據能夠在緩衝區先後移動。
IO中線程調用read()、write()是阻塞的,NIO非阻塞。
IO模型不斷輪詢socket狀態是用戶線程進行的,NIO輪詢每一個socket狀態是內核在進行的,這個效率要比用戶線程要高的多。
Netty介紹
Netty 是一個高性能、異步事件驅動的NIO框架,基於JAVA NIO提供的API實現。它提供了對TCP、UDP和文件傳輸的支持,Netty的全部IO操做都是異步非阻塞的,經過Future-Listener機制,用戶能夠方便的主動獲取或者經過通知機制得到IO操做結果。
Netty使用的堆外直接內存(零拷貝)不須要字節緩衝區的二次拷貝。傳統的JVM將堆內存拷貝直接內存,而後才寫入socket中,消息在發送過程當中多了一次拷貝。Netty提供了組合Buffer操做、文件傳輸採用transferTo方法,均可以將緩衝區數據在堆外內存直接操做,避免內存拷貝問題。