volatile概念:java
volatile關鍵字的主要做用是使變量在多個線程間可見,不具有原子性。
多個線程的可見性:volatile。使用netty。
原子性:AtomicLong等,注意atomic類只自己方法原子性,並不能保證屢次操做的原子。數組
線程之間的通訊:緩存
線程是操做系統中獨立的個體,但這些個體若是不通過特殊的處理就不能成爲一個總體,線程間的通訊就成爲總體的比用方式之一。
使用wait/notify方法實現線程間的通訊,注意這兩個方法都是object的類的方法,換句話說Java爲全部的對象都提供了這兩個方法。
1.wait和notify必須配合synchronized關鍵字使用
2.wait方法釋放鎖,notify方法不能釋放鎖。安全
final Object lock = new Object(); synchronized(lock){ lock.notify(); } synchronized(lock){ lock.wait(); }
wait()線程必須先啓動。
注意有個問題,不能實時的通知。
解決方法:final CountDownLatch countDownLatch = new CountDownLatch();
去掉synchronized,notify()改成:countDownLatch.countDown();
wait改成await();
使用wait/notify模擬網絡
Queue
BlockingQueue:它是一個隊列,而且支持阻塞的機制,阻塞的放入和獲得數據。實現put和take
ThreadLocal
單列 static inner class 最好。多線程
public Class innerStringletion(){ private static class Singletion{ private static Singletion single = new Singletion(); } public static Singletion getInstance(){ return Singletion.single(); } }
同步類容器
同步類容器都是線程安全的,但在某些場景下可能須要加鎖來保護複合操做。複合類操做:迭代、跳轉、以及條件運算。
古老的Vector、HashTable,這些容器的同步功能其實都是jdk的Collections.synchronized***等工廠方法去建立實現的。
其底層的機制無非就是用傳統的synchronized關鍵字對每一個公用的方法都進行同步,使得每次只能有一個線程訪問容器的狀態。不符合如今高併發的需求。
併發類容器
目的代替同步類容器
ConcurrentHashMap
CopyOnWriteArrayList
Queue,ConcurrentLinkedQueue
ConcurrentMap接口重要實現
ConcurrentHashMap
ConcurrentSkipListMap(支持併發排序功能)
ConcurrentHashMap:併發
內部使用段(Segment)來表示這些不一樣的部分,每一個段其實就是一個小的HashTable,他們有本身的鎖。
只要多個修改操做發生在不一樣的段上,它們就能夠併發進行。把一個總體分紅了16個段,也就是最高支持16個線程的併發修改操做。這也是
在多線程場景時減少鎖的粒度從而下降鎖競爭的一種方案。而且代碼中大多共享變量使用volatile關鍵字聲明。目的是第一時間獲取修改的內容。
CopyOnWrite:框架
容器即寫時複製容器。通俗的理解是當咱們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行copy,複製出
一個新的容器,而後新的容器裏添加元素,添加以後,再將原容器的引用指向新的容器。這樣作的好處是咱們能夠對copyonWrite容器進行併發的讀,
不須要加鎖,由於當前容器不會添加任何元素,因此copyonwrite容器也是一種讀寫分離的思想,讀和寫不一樣的容器。
使用場景:讀多寫少。寫的話,每次都須要拷貝新的容器消耗資源大。高併發
併發Queue性能
在併發隊列上提供了兩套實現,一個是以ConcurrentLinkedQueue爲表明的高性能隊列,一個是以BlockingQueue接口爲表明的阻塞隊列。
ConcurrentLinkedQueue:是一個適用高併發場景下的隊列,經過無鎖的方式,實現了高併發狀態下的高性能,一般ConcurrentLinkedQueue性能好於
BlockingQueue:它是一個基於鏈表節點的無界線程安全隊列。該隊列的元素遵循先進先出的原則。頭是最早加入的,尾是最近加入的,該隊列不容許null元素。
add()和offer()加入元素。
poll和peek都是取頭元素節點,區別在於前者會刪除元素,後者不會。
ArrayBlockingQueue:基於數組的阻塞隊列實現,在ArrayBlockingQueue內部,維護了一個定長的數組,以便緩存隊列中的數據對象,其內部沒實現讀寫分離
也就意味着生產和消費不能徹底並行,長度是須要定義的,能夠指定先進先出或者先進後出,也叫有界隊列,在不少場合很是適用。
LinkedBlockingQueue:跟上面的相反。無界隊列,生產和消費並行。可是都是阻塞的。
synchronousQueue:一種沒有緩衝的隊列。生產者產生的數據直接會被消費者獲取並消費掉。注意:先有take,才能add方法。
DelayQueue:帶有延遲時間的Queue,其中的元素只有當其指定的延遲時間到了,纔可以從隊列中獲取到該元素。DelayQueue中的元素必須實現Delayed接口,DelayQueue是
一個沒有大小限制的隊列,應用場景不少,好比對緩存超時的數據進行移除,任務超時處理,空閒鏈接的關閉等。
優先級隊列:在take的時候進行排序。
Future模式
有點相似於商品訂單。只要下完單,在家等着,中間的過程不用管。
客戶端Future FutureDate包裝類 RealData真實數據類
data對象------------->call----------------->other call
<------------call return
實際使用時,獲取數據 <-------------------call return
jdk有實現。
Master-Worker模式
Matser-worker模式是經常使用的並行計算模式。它的核心思想是系統有兩類進程協做工做:Master-worker進程。master負責接收的分配任務,
worker負責處理子任務。當各個worker子進程處理完成以後,會將結果返回給master,由master作概括和總結。其好處是能將一個大任務分解成若干個小任務
並行執行,從而提升系統的吞吐量。
生產者-消費者模式
在生產-消費者模式中:一般由兩類線程,即若干個生產者的線程和若干個消費者的線程。生產者線程負責提交用戶請求,消費者線程則負責具體處理生產者提交 的任務,在生產者和消費者之間經過共享內存緩存區進行通訊。
Executor框架
比較重要的類,Executors。
建立線程池的方法:
newFixedThreadPool()方法:該方法返回一個固定數量的線程池,該方法的線程數始終不變,當有一個任務提交時,若線程池中空閒,則當即執行,若沒有,則會被
暫緩在一個任務隊列中等待有空閒的線程去執行。
newSingleThreadExecutor()方法,建立一個線程的線程池,若空閒則執行,若沒有空閒線程則暫緩在任務隊列中。
newCachedThreadPool()方法,返回一個可根據實際狀況調整線程個數的線程池,不限制最大線程數量,若用空閒的線程則執行任務,若無任務則不建立線程。而且
每個空閒線程會在60秒後自動回收。
newScheduledThreadPool()方法,該方法返回一個SchededExecutorService對象,但該線程池能夠指定線程的數量。
自定義線程池使用詳細
在使用有界隊列時,如有新的任務須要執行,若是線程池實際線程數小於corePoolSize,則優先建立線程,若大於corePoolSize,則會將任務加入隊列,若隊列已滿,則在總線程數不大於maximumPoolSize的
前提下,建立新的線程,若線程數大於maximumPoolSize,則執行拒絕策略。或其餘自定義方式。
無界的任務隊列時:LinkedBlockingQueue。與有界隊列相比,除非系統耗盡資源,不然無界的隊列不存在任務入隊失敗的狀況。當有新任務到來,系統的線程數小於corePoolSize時,則新建線程執行任務。當達到
corePoolSize後,就不會繼續增長。若後續仍有新的任務增長,而沒有空閒的線程資源,則任務直接進入隊列等待。若任務建立個處理的速度差別很大,無界隊列會保持快速增加,直到內存系統耗盡。
JDK拒絕策略:
若是須要自定義拒絕策略能夠實現RejectedExecutionHandler接口。
線程的大小:corePoolSize+隊列的數量,有界隊列。
高併發:網絡端、服務(分流)、Java(限流)
Concurrent.util類
CyclicBarrier的使用:
假設有隻有的一個場景:每一個線程表明一個跑步運動員,當運動員都準備好後,才一塊兒出發,只有一我的沒有準備好,你們都等待。
CountDownLatch使用:
它常常用於監聽某些初始化操做,等待初始化執行完畢後,通知主線程繼續工做。
Callable和Future使用:
就是以前的future模式。很是適合在處理很耗時很長的業務邏輯時進行使用,能夠有效的減小系統的響應時間,提升系統的吞吐量。
Semaphone信號量(限流)
PV:網站的總訪問量,頁面瀏覽量或點擊量,用戶每刷新一次就會被記錄一次。
UV:訪問網站的一臺電腦客戶端爲一個訪客。在0點到24小時以內根據IP爲一次。
QPS:每秒查詢數,qps很大程度上表明瞭系統業務上的繁忙程度。每次請求的背後,可能對應着屢次磁盤I/O,屢次網絡請求,多個CPU時間片等。咱們經過qps能夠很是直觀的
瞭解當前系統業務狀況,一qps超過鎖設定的預警閾值。能夠考慮增長機器對集羣擴容。
RT:請求的響應時間。
容量評估:進行多倫壓力測試以後,能夠對系統進行峯值評估,採用所謂的60/20原則,即80%的訪問請求將在20%的時間內達到。
峯值qps=(總pv * 80%)/(60 * 60 * 24 * 20%)
機器所需數量:總的峯值qps/壓測的出的單機極限qps
拿到信號量的線程可進入,不然等待。經過acquire和release獲取和釋放訪問許可。
鎖
重入鎖和讀寫鎖 ReentrantLock(重入鎖)。在須要進行同步的代碼加上鎖定,但不要忘記最後必定要釋放鎖定。 同時可使用一個新的等待/通知的類,它就是Condition。這個Condition必定是針對具體某一把鎖。也就是在只有鎖的基礎之上纔會產生Condition。 Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); Condition condition1 = lock.newCondition(); condition.await()至關於await(); condition.single();至關於通知notify(). ReentrantReadWriteLock(讀寫鎖) 其核心就是實現讀寫分離的鎖,在高併發訪問下,尤爲是讀多寫少的狀況下,性能遠高於重入鎖 咱們知道,同一時間內,只能有一個線程進行訪問被鎖定的代碼,那麼讀寫鎖則不一樣,其本質是分紅兩個鎖,即讀鎖、寫鎖、在讀鎖下多個線程能夠併發的進行訪問,可是在寫鎖的時候,只能一個個的順序訪問。 生產者應該放在容器裏。