1.synchronized之錯誤的加鎖和原因分析
-
鎖一定要加在一個不變的對象上
volatile 關鍵字穿稳,最輕量的同步機制
volatile 保證了不同線程對這個變量進行操作時的可見性叁熔,即一個線程修改了某 個變量的值,這新值對其他線程來說是立即可見的证九。
可以保證可見彭谁,但是不能保證原子
volatile 最適用的場景:一個線程寫吸奴,多個線程讀。
2.ThreadLocal辨析
ThreadLocal 為每個線程都提供了變量的副本缠局,使得每個線程在某一時間訪問到的并非同一個對象则奥,這樣就隔離了多個線程對數(shù)據的數(shù)據共享
- void set(Object value) 設置當前線程的線程局部變量的值。
- public Object get() 該方法返回當前線程所對應的線程局部變量狭园。
- public void remove() 將當前線程局部變量的值刪除读处,目的是為了減少內存的占用。
- protected Object initialValue()返回該線程局部變量的初始值唱矛,該方法是一個 protected 的方法罚舱,顯然是為 了讓子類覆蓋而設計的。
每個線程獨有的 ThreadLocalMap 然后再用 ThreadLocal 的當前實例绎谦,拿到 Map 中的相應的 Entry管闷,然后就可 以拿到相應的值返回出去。當然窃肠,如果 Map 為空包个,還會先進行map 的創(chuàng)建,初始化等工作冤留。
-
ThreadLocal可能會產生內存泄漏
圖中的虛線表示弱引用赃蛛。
這樣恃锉,當把 threadlocal 變量置為 null 以后,沒有任何強引用指向 threadlocal 實例呕臂,所以 threadlocal 將會被 gc 回收。這樣一來肪跋,ThreadLocalMap 中就會出現(xiàn) key 為 null 的 Entry歧蒋,就沒有辦法訪問這些 key 為 null 的 Entry 的 value,如果當前 線程再遲遲不結束的話州既,這些 key 為 null 的 Entry 的 value 就會一直存在一條強 引用鏈:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value谜洽,而這塊 value 永 遠不會被訪問到了,所以存在著內存泄露吴叶。
ThreadLocal 內存泄漏的根源是:由于 ThreadLocalMap 的生命周期跟 Thread 一樣長阐虚,如果沒有手動刪除對應 key 就會導致內存泄漏,而不是因為弱引 用蚌卤∈凳總結 JVM 利用設置 ThreadLocalMap 的 Key 為弱引用,來避免內存泄露逊彭。 JVM 利用調用 remove咸灿、get、set 方法的時候侮叮,回收弱引用避矢。 當 ThreadLocal 存儲很多 Key 為 null 的 Entry 的時候,而不再去調用 remove囊榜、 get审胸、set 方法,那么將導致內存泄漏卸勺。 使用線程池+ ThreadLocal 時要小心砂沛,因為這種情況下,線程是一直在不斷的 重復運行的孔庭,從而也就造成了 value 可能造成累積的情況尺上。
- ThreadLocal線程不安全的情況
每個線程中的 ThreadLocal 都應該持有一個新的 Number 對象。
3.線程之間的協(xié)作
等待和通知
wait():調用該方法的線程進入 WAITING 狀態(tài),只有等待另外線程的通知或被中斷 才會返回.需要注意,調用 wait()方法后,會釋放對象的鎖
notify/notifyAll
通知一個在對象上等待的線程,使其從 wait 方法返回,而返回的前提是該線程 獲取到了對象的鎖圆到,沒有獲得鎖的線程重新進入 WAITING 狀態(tài)怎抛。-
等待和通知的標準范式
等待方遵循如下原則。
1)獲取對象的鎖芽淡。
2)如果條件不滿足马绝,那么調用對象的 wait()方法,被通知后仍要檢查條件挣菲。
3)條件滿足則執(zhí)行對應的邏輯富稻。
通知方遵循如下原則掷邦。
1)獲得對象的鎖。
2)改變條件椭赋。
3)通知所有等待在對象上的線程抚岗。
notify 和 notifyAll 應該用誰 盡可能用 notifyall(),謹慎使用 notify()哪怔,因為 notify()只會喚醒一個線程宣蔚,我 們無法確保被喚醒的這個線程一定就是我們需要喚醒的線程
調用 yield() 、sleep()认境、wait()胚委、notify()等方法對鎖有何影響?
yield() 叉信、sleep()被調用后亩冬,都不會釋放當前線程所持有的鎖。 調用 wait()方法后硼身,會釋放當前線程持有的鎖硅急,而且當前被喚醒后,會重新 去競爭鎖鸠姨,鎖競爭到后才會執(zhí)行 wait 方法后面的代碼铜秆。 調用 notify()系列方法后,對鎖無影響讶迁,線程只有在 syn 同步代碼執(zhí)行完后才 會自然而然的釋放鎖连茧,所以 notify()系列方法一般都是 syn 同步代碼的最后一行。
4.線程的并發(fā)工具類
-
Fork-Join(分而治之的算法)
-
工作密取
當前線程的 Task 已經全被執(zhí)行完畢巍糯,則自動取到其他線程的 Task 池中取 出 Task 繼續(xù)執(zhí)行啸驯。
-
Fork/Join 使用的標準范式
CountDownLatch(閉鎖)
閉鎖,CountDownLatch 這個類能夠使一個線程等待其他線程完成各自的工 作后再執(zhí)行祟峦。例如罚斗,應用程序的主線程希望在負責啟動框架服務的線程已經啟動 所有的框架服務之后再執(zhí)行。
CountDownLatch 是通過一個計數(shù)器來實現(xiàn)的宅楞,計數(shù)器的初始值為初始任務 的數(shù)量针姿。每當完成了一個任務后,計數(shù)器的值就會減 1 (CountDownLatch.countDown()方法)厌衙。當計數(shù)器值到達 0 時距淫,它表示所有的已 經完成了任務,然后在閉鎖上等待 CountDownLatch.await()方法的線程就可以恢 復執(zhí)行任務婶希。CyclicBarrier
會讓所有線程都等待完成后才會繼續(xù)下一步行動Semaphore
Semaphore 通常我們叫它信號量榕暇, 可以用來控制同時訪問特定資源的線程數(shù)量,通過協(xié)調各個線程,以保證合理的使用資源彤枢。Exchange
Exchange位于java.util.concurrent包下面狰晚,主要是用于線程之間數(shù)據交換的工具類,經常用于管道設計和遺傳算法中
6.線程的狀態(tài)
- 初始(new)
- 運行中(running)
- 就緒(ready)
- 等待(waiting)
- 等待超時(timed_waiting)
- 終止(terminated)
- 阻塞(blocked)
- 死鎖
死鎖是指兩個或兩個以上的進程在執(zhí)行過程中缴啡,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象壁晒,若無外力作用,它們都將無法推進下去盟猖。此時稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產生了死鎖讨衣。
產生條件:
1.多個操作者爭奪多個資源
2.爭奪資源的順序不對
3.拿到資源不放手
學術說法:
1.互斥條件
2.請求和保持
3.不剝奪
4.環(huán)路等待
活鎖
兩個線程在嘗試拿鎖的機制中,發(fā)生多個線程之間互相謙讓式镐,不斷發(fā)生同一個線程總是拿到同一把鎖,在嘗試拿另一把鎖時因為拿不到固蚤,而將本來已經持有的鎖釋放的過程娘汞。
解決辦法:每個線程休眠隨機數(shù),錯開拿鎖的時間夕玩。線程饑餓
低優(yōu)先級的線程你弦,總是拿不到執(zhí)行時間
7.CAS(Compare And Swap)
什么是原子操作?如何實現(xiàn)原子操作燎孟?
假定有兩個操作A和B(A和B可能都很復雜)禽作,如果從執(zhí)行A的線程來看,當另一個線程執(zhí)行B時揩页,要么將B全部執(zhí)行完旷偿,要么完全不執(zhí)行B,那么A和B對彼此來說是原子的爆侣。
synchronized關鍵字是基于阻塞的鎖機制萍程,也就是說當一個線程擁有鎖的時候,訪問同一資源的其它線程需要等待兔仰,直到該線程釋放鎖CAS原理
基本思路就是茫负,如果這個地址上的值和期望的值相等,則給其賦予新值乎赴,否則不做任何事兒忍法,但是要返回原值是多少。循環(huán)CAS就是在一個循環(huán)里不斷的做cas操作榕吼,直到成功為止饿序。
synchronized 悲觀鎖
CAS 樂觀鎖
CAS的問題
1.ABA問題
因為CAS需要在操作值的時候,檢查值有沒有發(fā)生變化友题,如果沒有發(fā)生變化則更新嗤堰,但是如果一個值原來是A,變成了B,又變成了A踢匣,那么使用CAS進行檢查時會發(fā)現(xiàn)它的值沒有發(fā)生變化告匠,但是實際上卻變化了。
ABA問題的解決思路就是使用版本號离唬。在變量前面追加上版本號后专,每次變量更新的時候把版本號加1,那么A→B→A就會變成1A→2B→3A输莺。
2.循環(huán)時間長開銷大戚哎。
自旋CAS如果長時間不成功,會給CPU帶來非常大的執(zhí)行開銷嫂用。
3.只能保證一個共享變量的原子操作型凳。Jdk中相關原子操作類的使用
更新基本類型類:AtomicBoolean,AtomicInteger嘱函,AtomicLong
更新數(shù)組類:AtomicIntegerArray甘畅,AtomicLongArray,AtomicReferenceArray
更新引用類型:AtomicReference往弓,AtomicMarkableReference疏唾,AtomicStampedReference