CAS(Compare And Swap)比較並轉換
該算法涉及三個數:內存值V,舊的預期值A,新的預期值B。當且僅當舊的預期值A和內存值V相同時,將內存值改成B,不然什麼也不作。 html
CAS遺留問題:程序員
(1)CAS自旋,舊的指望值與內存值不等,從新加載值進行運算直到舊的指望值等於內存值, 浪費CPU;算法
(2)ABA問題;編程
AutomicInteger基於CAS實現樂觀鎖數組
Java的併發採用的是共享內存模型,通訊老是隱式進行,整個通訊過程對程序員徹底透明。緩存
線程之間的共享變量存儲在主內存(Main Memory)中,每一個線程都有一個私有的本地內存(Local Memory),本地內存中存儲了該線程以讀/寫共享變量的副本。本地內存是JMM的一個抽象概念,並不真實存在。
安全
處理器重排序(二、3),JMM的處理器重排序規則會要求Java編譯器在生成指令序列時,插入特定類型的內存屏障(Memory Barriers,Intel稱之爲Memory Fence)指令,經過內存屏障指令來禁止特定類型的處理器重排序。多線程
內存屏障類型表:
併發
as-if-serial:無論怎麼重排序(編譯器和處理器爲了提升並行度),(單線程)程序的執行結果不能被改變。編譯器、runtime和處理器都必須遵照as-if-serial語義。app
爲了遵照as-if-serial語義,編譯器和處理器不會對存在數據依賴關係的操做作重排序。
關鍵字volatile能夠用來修飾字段(成員變量),就是告知程序任何對該變量的訪問均須要
從共享內存中獲取,而對它的改變必須同步刷新回共享內存,它能保證全部線程對變量訪問
的可見性。
JMM採起保守策略
(1)在每一個volatile寫操做的前面插入一個StoreStore屏障。
(2)在每一個volatile寫操做的後面插入一個StoreLoad屏障。
(3)在每一個volatile讀操做的後面插入一個LoadLoad屏障。
(4)在每一個volatile讀操做的後面插入一個LoadStore屏障。
鎖除了讓臨界區互斥執行外,還可讓釋放鎖的線程向獲取同一個鎖的線程發送消息(經過主內存)。
ReentrantLock的實現依賴於Java同步器框架AbstractQueuedSynchronizer(本文簡稱之爲AQS)。AQS使用一個整型的volatile變量(命名爲state)來維護同步狀態。
公平鎖:先運行的線程,那就能夠先獲得鎖。
非公平鎖:新晉獲取鎖的進程會有屢次機會去搶佔鎖。若是被加入了等待隊列後則跟公平鎖沒有區別。
ReentrantLock源碼解析:https://www.cnblogs.com/leesf456/p/5383609.html
讀final域的重排序規則能夠確保:在讀一個對象的final域以前,必定會先讀包含這個final域的對象的引用。
寫final域的重排序規則能夠確保:在引用變量爲任意線程可見以前,該引用變量指向的對象的final域已經在構造函數中被正確初始化過了。要獲得這個效果,還須要一個保證:在構造函數內部,不能讓這個被構造對象的引用爲其餘線程所見,也就是對象引用不能在構造函數中「逸出」。
在構造函數返回前,被構造對象的引用不能爲其餘線程所見,由於此時的final域可能尚未被初始化。在構造函數返回後,任意線程都將保證能看到final域正確初始化以後的值
在JMM中,若是一個操做執行的結果須要對另外一個操做可見,那麼這兩個操做之間必需要存在happens-before關係。這裏提到的兩個操做既能夠是在一個線程以內,也能夠是在不一樣線程之間。
happens-before規則:
(1)程序順序規則:一個線程中的每一個操做,happens-before於該線程中的任意後續操做。
(2)監視器鎖規則:對一個鎖的解鎖,happens-before於隨後對這個鎖的加鎖。
(3)volatile變量規則:對一個volatile域的寫,happens-before於任意後續對這個volatile域的讀。
(4)傳遞性:若是A happens-before B,且B happens-before C,那麼A happens-before C。
操做系統在運行一個程序時,會爲其建立一個進程。操做系統調度的最小單元是線程,也叫輕量級進程。
線程優先級
在Java線程中,經過一個整型成員變量priority來控制優先級,優先級的範圍從1~10,在線程構建的時候能夠經過setPriority(int)方法來修改優先級,默認優先級是5,優先級高的線程分配時間片的數量要多於優先級低的線程。設置線程優先級時,針對頻繁阻塞(休眠或者I/O操做)的線程須要設置較高優先級,而偏重計算(須要較多CPU時間或者偏運算)的線程則設置較低的優先級,確保處理器不會被獨佔。
Daemon線程是一種支持型線程,由於它主要被用做程序中後臺調度以及支持性工做。這意味着,當一個Java虛擬機中不存在非Daemon線程的時候,Java虛擬機將會退出。能夠經過調用Thread.setDaemon(true)將線程設置爲Daemon線程。
注意 Daemon屬性須要在啓動線程以前設置,不能在啓動線程以後設置
synchronize其本質是對一 個對象的監視器(monitor)進行獲取,而這個獲取過程是排他的,也就是同一時刻只能有一個 線程獲取到由synchronized所保護對象的監視器。任意一個對象都擁有本身的監視器,當這個對象由同步塊或者這個對象的同步方法調用 時,執行方法的線程必須先獲取到該對象的監視器才能進入同步塊或者同步方法,而沒有獲 取到監視器(執行該方法)的線程將會被阻塞在同步塊和同步方法的入口處,進入BLOCKED 狀態。
1)使用wait()、notify()和notifyAll()時須要先對調用對象加鎖。
2)調用wait()方法後,線程狀態由RUNNING變爲WAITING,並將當前線程放置到對象的 等待隊列。
3)notify()或notifyAll()方法調用後,等待線程依舊不會從wait()返回,須要調用notify()或 notifAll()的線程釋放鎖以後,等待線程纔有機會從wait()返回。
4)notify()方法將等待隊列中的一個等待線程從等待隊列中移到同步隊列中,而notifyAll() 方法則是將等待隊列中全部的線程所有移到同步隊列,被移動的線程狀態由WAITING變爲 BLOCKED。
5)從wait()方法返回的前提是得到了調用對象的鎖。
Thread.join()其含義是:若是一個線程A執行了thread.join()語句,當前線程A等待thread線程終止以後才 從thread.join()返回。線程Thread除了提供join()方法以外,還提供了join(long millis)和join(long millis,int nanos)兩個具有超時特性的方法。
lock(); tryLock(); tryLock(long time, TimeUnit unit);unLock;
隊列同步器AbstractQueuedSynchronizer(如下簡稱同步器),是用來構建鎖或者其餘同步組 件的基礎框架,它使用了一個int成員變量表示同步狀態,經過內置的FIFO隊列來完成資源獲 取線程的排隊工做。
實現分析
(1)同步隊列
(2)獨佔式同步狀態獲取與釋放
總結:在獲取同步狀態時,同步器維 護一個同步隊列,獲取狀態失敗的線程都會被加入到隊列中並在隊列中進行自旋;移出隊列 (或中止自旋)的條件是前驅節點爲頭節點且成功獲取了同步狀態。在釋放同步狀態時,同步 器調用tryRelease(int arg)方法釋放同步狀態,而後喚醒頭節點的後繼節點。
(3)共享式同步狀態獲取與釋放
共享式訪問資源時,其餘共享式的訪問均被容許,而獨佔式訪問被 阻塞,右半部分是獨佔式訪問資源時,同一時刻其餘訪問均被阻塞。
(4)獨佔式超時獲取同步狀態
獨佔式超時獲取同步狀態doAcquireNanos(int arg,long nanosTimeout) 和獨佔式獲取同步狀態acquire(int args)在流程上很是類似,其主要區別在於未獲取到同步狀 態時的處理邏輯。acquire(int args)在未獲取到同步狀態時,將會使當前線程一直處於等待狀 態,而doAcquireNanos(int arg,long nanosTimeout)會使當前線程等待nanosTimeout納秒,若是當 前線程在nanosTimeout納秒內沒有獲取到同步狀態,將會從等待邏輯中自動返回。
(5)自定義同步組件--TwinsLock
重入鎖ReentrantLock,顧名思義,就是支持重進入的鎖,它表示該鎖可以支持一個線程對 資源的重複加鎖。除此以外,該鎖的還支持獲取鎖時的公平和非公平性選擇。
以前提到鎖(如Mutex和ReentrantLock)基本都是排他鎖,這些鎖在同一時刻只容許一個線 程進行訪問,而讀寫鎖在同一時刻能夠容許多個讀線程訪問,可是在寫線程訪問時,全部的讀 線程和其餘寫線程均被阻塞。讀寫鎖維護了一對鎖,一個讀鎖和一個寫鎖,經過分離讀鎖和寫 鎖,使得併發性相比通常的排他鎖有了很大提高。
鎖降級是指把持住(當前擁有的)寫鎖,再獲取到 讀鎖,隨後釋放(先前擁有的)寫鎖的過程。
LockSupport定義了一組的公共靜態方法,這些方法提供了最基本的線程阻塞和喚醒功 能,而LockSupport也成爲構建同步組件的基礎工具。LockSupport定義了一組以park開頭的方法用來阻塞當前線程,以及unpark(Thread thread) 方法來喚醒一個被阻塞的線程。
Condition接口也提供了相似Object的監視器方法,與Lock配合能夠實現等 待/通知模式。
等待隊列
等待:調用Condition的await()方法(或者以await開頭的方法),會使當前線程進入等待隊列並釋 放鎖,同時線程狀態變爲等待狀態。當從await()方法返回時,當前線程必定獲取了Condition相 關聯的鎖。 若是從隊列(同步隊列和等待隊列)的角度看await()方法,當調用await()方法時,至關於同 步隊列的首節點(獲取了鎖的節點)移動到Condition的等待隊列中。
ConcurrentHashMap是線程安全且高效的HashMap,鎖分段技術。
ConcurrentLinkedQueue是一個基於連接節點的無界線程安全隊列,它採用先進先出的規 則對節點進行排序,當咱們添加一個元素的時候,它會添加到隊列的尾部;當咱們獲取一個元 素時,它會返回隊列頭部的元素。它採用了「wait-free」算法(即CAS算法)來實現,該算法在 Michael&Scott算法上進行了一些修改。
阻塞隊列(BlockingQueue)是一個支持兩個附加操做的隊列。這兩個附加的操做支持阻塞 的插入和移除方法。 1)支持阻塞的插入方法:意思是當隊列滿時,隊列會阻塞插入元素的線程,直到隊列不 滿。 2)支持阻塞的移除方法:意思是在隊列爲空時,獲取元素的線程會等待隊列變爲非空。
ArrayBlockingQueue是一個用數組實現的有界阻塞隊列。此隊列按照先進先出(FIFO)的原 則對元素進行排序。
LinkedBlockingQueue是一個用鏈表實現的有界阻塞隊列。此隊列的默認和最大長度爲 Integer.MAX_VALUE。此隊列按照先進先出的原則對元素進行排序。
PriorityBlockingQueue是一個支持優先級的無界阻塞隊列。默認狀況下元素採起天然順序 升序排列。也能夠自定義類實現compareTo()方法來指定元素排序規則,或者初始化 PriorityBlockingQueue時,指定構造參數Comparator來對元素進行排序。須要注意的是不能保證 同優先級元素的順序。
DelayQueue是一個支持延時獲取元素的無界阻塞隊列。隊列使用PriorityQueue來實現。隊 列中的元素必須實現Delayed接口,在建立元素時能夠指定多久才能從隊列中獲取當前元素。 只有在延遲期滿時才能從隊列中提取元素。
緩存系統的設計:能夠用DelayQueue保存緩存元素的有效期,使用一個線程循環查詢 DelayQueue,一旦能從DelayQueue中獲取元素時,表示緩存有效期到了。
定時任務調度:使用DelayQueue保存當天將會執行的任務和執行時間,一旦從 DelayQueue中獲取到任務就開始執行,好比TimerQueue就是使用DelayQueue實現的。
SynchronousQueue是一個不存儲元素的阻塞隊列。每個put操做必須等待一個take操做, 不然不能繼續添加元素。 支持公平訪問隊列。默認狀況下線程採用非公平性策略訪問隊列。SynchronousQueue能夠當作是一個傳球手,負責把生產者線程處理的數據直接傳遞給消費 者線程。隊列自己並不存儲任何元素,很是適合傳遞性場景。SynchronousQueue的吞吐量高於 LinkedBlockingQueue和ArrayBlockingQueue。
LinkedTransferQueue是一個由鏈表結構組成的無界阻塞TransferQueue隊列。相對於其餘阻 塞隊列,LinkedTransferQueue多了tryTransfer和transfer方法。
LinkedBlockingDeque是一個由鏈表結構組成的雙向阻塞隊列。所謂雙向隊列指的是能夠 從隊列的兩端插入和移出元素。
ArrayBlockingQueue實現原理
Fork/Join框架是Java 7提供的一個用於並行執行任務的框架,是一個把大任務分割成若干 個小任務,最終彙總每一個小任務結果後獲得大任務結果的框架。
工做竊取(work-stealing)算法是指某個線程從其餘隊列裏竊取任務來執行。
工做竊取算法的優勢:充分利用線程進行並行計算,減小了線程間的競爭。
工做竊取算法的缺點:在某些狀況下仍是存在競爭,好比雙端隊列裏只有一個任務時。而且該算法會消耗了更多的系統資源,好比建立多個線程和多個雙端隊列。
ForkJoinPool由ForkJoinTask數組和ForkJoinWorkerThread數組組成,ForkJoinTask數組負責 將存放程序提交給ForkJoinPool的任務,而ForkJoinWorkerThread數組負責執行這些任務。
(1)ForkJoinTask的fork方法實現原理 當咱們調用ForkJoinTask的fork方法時,程序會調用ForkJoinWorkerThread的pushTask方法 異步地執行這個任務,而後當即返回結果。pushTask方法把當前任務存放在ForkJoinTask數組隊列裏。而後再調用ForkJoinPool的 signalWork()方法喚醒或建立一個工做線程來執行任務。
(2)ForkJoinTask的join方法實現原理
Join方法的主要做用是阻塞當前線程並等待獲取結果。調用了doJoin()方法,經過doJoin()方法獲得當前任務的狀態來判斷返回什麼結 果,任務狀態有4種:已完成(NORMAL)、被取消(CANCELLED)、信號(SIGNAL)和出現異常 (EXCEPTIONAL)。 ·若是任務狀態是已完成,則直接返回任務結果。 ·若是任務狀態是被取消,則直接拋出CancellationException。 ·若是任務狀態是拋出異常,則直接拋出對應的異常。在doJoin()方法裏,首先經過查看任務的狀態,看任務是否已經執行完成,若是執行完成, 則直接返回任務狀態;若是沒有執行完,則從任務數組裏取出任務並執行。若是任務順利執行 完成,則設置任務狀態爲NORMAL,若是出現異常,則記錄異常,並將任務狀態設置爲 EXCEPTIONAL。
AtomicBoolean:原子更新布爾類型。
AtomicInteger:原子更新整型。
AtomicLong:原子更新長整型。
AtomicIntegerArray:原子更新整型數組裏的元素。
AtomicLongArray:原子更新長整型數組裏的元素。
AtomicReferenceArray:原子更新引用類型數組裏的元素。
AtomicReference:原子更新引用類型。
AtomicReferenceFieldUpdater:原子更新引用類型裏的字段。
AtomicMarkableReference:原子更新帶有標記位的引用類型。能夠原子更新一個布爾類 型的標記位和引用類型。構造方法是AtomicMarkableReference(V initialRef,boolean initialMark)。
AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
AtomicLongFieldUpdater:原子更新長整型字段的更新器。
AtomicStampedReference:原子更新帶有版本號的引用類型。該類將整數值與引用關聯起 來,可用於原子的更新數據和數據的版本號,能夠解決使用CAS進行原子更新時可能出現的 ABA問題。
CountDownLatch容許一個或多個線程等待其餘線程完成操做。
CyclicBarrier的字面意思是可循環使用(Cyclic)的屏障(Barrier)。它要作的事情是,讓一 組線程到達一個屏障(也能夠叫同步點)時被阻塞,直到最後一個線程到達屏障時,屏障纔會 開門,全部被屏障攔截的線程纔會繼續運行。
Semaphore(信號量)是用來控制同時訪問特定資源的線程數量,它經過協調各個線程,以 保證合理的使用公共資源。
Exchanger(交換者)是一個用於線程間協做的工具類。Exchanger用於進行線程間的數據交 換。它提供一個同步點,在這個同步點,兩個線程能夠交換彼此的數據。這兩個線程經過 exchange方法交換數據,若是第一個線程先執行exchange()方法,它會一直等待第二個線程也 執行exchange方法,當兩個線程都到達同步點時,這兩個線程就能夠交換數據,將本線程生產 出來的數據傳遞給對方。
在開發過程當中,合理地使用線程池可以帶來3個好處。 第一:下降資源消耗。經過重複利用已建立的線程下降線程建立和銷燬形成的消耗。 第二:提升響應速度。當任務到達時,任務能夠不須要等到線程建立就能當即執行。 第三:提升線程的可管理性。線程是稀缺資源,若是無限制地建立,不只會消耗系統資源, 還會下降系統的穩定性,使用線程池能夠進行統一分配、調優和監控。
ThreadPoolExecutor執行execute方法分下面4種狀況。
1)若是當前運行的線程少於corePoolSize,則建立新線程來執行任務(注意,執行這一步驟 須要獲取全局鎖)。
2)若是運行的線程等於或多於corePoolSize,則將任務加入BlockingQueue。
3)若是沒法將任務加入BlockingQueue(隊列已滿),則建立新的線程來處理任務(注意,執 行這一步驟須要獲取全局鎖)。
4)若是建立新線程將使當前運行的線程超出maximumPoolSize,任務將被拒絕,並調用 RejectedExecutionHandler.rejectedExecution()方法。
Java的線程既是工做單元,也是執行機制。從JDK 5開始,把工做單元與執行機制分離開 來。工做單元包括Runnable和Callable,而執行機制由Executor框架提供。
Executor框架主要由3大部分組成以下。
(1)任務。包括被執行任務須要實現的接口:Runnable接口或Callable接口。
(2)任務的執行。包括任務執行機制的核心接口Executor,以及繼承自Executor的 ExecutorService接口。Executor框架有兩個關鍵類實現了ExecutorService接口 (ThreadPoolExecutor和ScheduledThreadPoolExecutor)。
(3)異步計算的結果。包括接口Future和實現Future接口的FutureTask類。