注:本文主要是總結(jié)下多線程椿每、并發(fā)編程、鎖編程相關(guān)概念挖诸,內(nèi)容相對(duì)簡(jiǎn)略法精。
注:如有侵權(quán)亿虽,請(qǐng)聯(lián)系刪除洛勉。
1收毫、什么是多線程
線程是程序執(zhí)行的最小單位此再,多個(gè)線程并發(fā)或并行執(zhí)行就是多線程。多線程下需要保證執(zhí)行結(jié)果的最終正確性玲销。
并發(fā):兩個(gè)及兩個(gè)以上的作業(yè)在同一 時(shí)間段 內(nèi)執(zhí)行输拇。
并行:兩個(gè)及兩個(gè)以上的作業(yè)在同一 時(shí)刻 執(zhí)行。
2贤斜、為什么使用多線程
充分發(fā)揮多核性能優(yōu)勢(shì)
單一線程同步執(zhí)行IO操作時(shí)策吠,存在長(zhǎng)時(shí)間的阻塞,無(wú)法充分使用CPU
3瘩绒、多線程下可能導(dǎo)致什么問(wèn)題
根本問(wèn)題:最終結(jié)果與單線程執(zhí)行結(jié)果不一致猴抹。
常見(jiàn)衍生問(wèn)題:
死鎖
內(nèi)存泄漏
線程不安全
...
4、線程模型:用戶線程和內(nèi)核線程之間的關(guān)聯(lián)方式
多對(duì)一蟀给、一對(duì)一、多對(duì)多
5阳堕、線程生命周期和狀態(tài):
初始狀態(tài)
運(yùn)行狀態(tài)
阻塞狀態(tài)
等待狀態(tài)
超時(shí)等待狀態(tài)
結(jié)束狀態(tài)
6跋理、線程間通信
管道流
等待/通知機(jī)制
共享內(nèi)存
threadLocal
7、線程上下文切換是什么恬总,什么場(chǎng)景下發(fā)生前普?
線程上下文切換是指在特定場(chǎng)景下,在CPU上執(zhí)行的線程發(fā)生變化越驻,需要記錄上個(gè)線程的執(zhí)行狀態(tài)汁政, 以及恢復(fù)新線程之前的執(zhí)行狀態(tài)道偷。
線程主動(dòng)讓出cpu
線程CPU時(shí)間片用盡,系統(tǒng)調(diào)度防止其他線程餓死记劈。
系統(tǒng)中斷
線程任務(wù)執(zhí)行完成
8勺鸦、死鎖四個(gè)必要條件:
1、互斥條件
2目木、不可剝奪條件
3换途、請(qǐng)求與保持條件
4、循環(huán)等待
9刽射、JMM
指令重排序:JIT編譯優(yōu)化 + CPU指令重排序
happenBefore原則
線程安全三大特性:原子性军拟、可見(jiàn)性、有序性
10誓禁、CAS(Compare-And-Swap, 比較并交換)
在 Java 中懈息,實(shí)現(xiàn) CAS(Compare-And-Swap, 比較并交換)操作的一個(gè)關(guān)鍵類是Unsafe
。
CAS 算法存在以下問(wèn)題
-
ABA 問(wèn)題是 CAS 算法最常見(jiàn)的問(wèn)題摹恰。
ABA 問(wèn)題的解決思路是在變量前面追加上版本號(hào)或者時(shí)間戳辫继。JDK 1.5 以后的
AtomicStampedReference
類就是用來(lái)解決 ABA 問(wèn)題的,其中的compareAndSet()
方法就是首先檢查當(dāng)前引用是否等于預(yù)期引用俗慈,并且當(dāng)前標(biāo)志是否等于預(yù)期標(biāo)志姑宽,如果全部相等,則以原子方式將該引用和該標(biāo)志的值設(shè)置為給定的更新值闺阱。 -
循環(huán)時(shí)間長(zhǎng)開(kāi)銷大
依照使用場(chǎng)景來(lái)選擇是否使用CAS炮车,寫多讀少,用鎖酣溃,讀多寫少瘦穆, 用CAS
-
只能保證一個(gè)共享變量的原子操作
CAS 操作僅能對(duì)單個(gè)共享變量有效。當(dāng)需要操作多個(gè)共享變量時(shí)救拉,CAS 就顯得無(wú)能為力难审。不過(guò),從 JDK 1.5 開(kāi)始亿絮,Java 提供了
AtomicReference
類告喊,這使得我們能夠保證引用對(duì)象之間的原子性。通過(guò)將多個(gè)變量封裝在一個(gè)對(duì)象中派昧,我們可以使用AtomicReference
來(lái)執(zhí)行 CAS 操作黔姜。
11、synchronized 關(guān)鍵字
在 Java 6 之后蒂萎, synchronized
引入了大量的優(yōu)化如自旋鎖秆吵、適應(yīng)性自旋鎖、鎖消除五慈、鎖粗化纳寂、偏向鎖主穗、輕量級(jí)鎖等技術(shù)來(lái)減少鎖操作的開(kāi)銷,實(shí)現(xiàn)核心原理是對(duì)象monitor的獲取毙芜,對(duì)象頭中有鎖標(biāo)志位忽媒。
12、ReentrantLock
ReentrantLock 比 synchronized 增加了一些高級(jí)功能
公平與非公平實(shí)現(xiàn)
可中斷
可選擇通知
13腋粥、ThreadLocal
ThreadLocal原理
最終的變量是放在了當(dāng)前線程的 ThreadLocalMap
中晦雨,并不是存在 ThreadLocal
上,ThreadLocal
可以理解為只是ThreadLocalMap
的封裝隘冲,傳遞了變量值闹瞧。 ThrealLocal
類中可以通過(guò)Thread.currentThread()
獲取到當(dāng)前線程對(duì)象后,直接通過(guò)getMap(Thread t)
可以訪問(wèn)到該線程的ThreadLocalMap
對(duì)象展辞。
每個(gè)Thread
中都具備一個(gè)ThreadLocalMap
奥邮,而ThreadLocalMap
可以存儲(chǔ)以ThreadLocal
為 key ,Object 對(duì)象為 value 的鍵值對(duì)罗珍。
ThreadLocal 內(nèi)存泄露問(wèn)題
ThreadLocalMap
中使用的 key 為 ThreadLocal
的弱引用漠烧,而 value 是強(qiáng)引用。所以靡砌,如果 ThreadLocal
沒(méi)有被外部強(qiáng)引用的情況下,在垃圾回收的時(shí)候珊楼,key 會(huì)被清理掉通殃,而 value 不會(huì)被清理掉。
這樣一來(lái)厕宗,ThreadLocalMap
中就會(huì)出現(xiàn) key 為 null 的 Entry画舌。假如我們不做任何措施的話,value 永遠(yuǎn)無(wú)法被 GC 回收已慢,這個(gè)時(shí)候就可能會(huì)產(chǎn)生內(nèi)存泄露曲聂。ThreadLocalMap
實(shí)現(xiàn)中已經(jīng)考慮了這種情況,在調(diào)用 set()
佑惠、get()
朋腋、remove()
方法的時(shí)候,會(huì)清理掉 key 為 null 的記錄膜楷。使用完 ThreadLocal
方法后最好手動(dòng)調(diào)用remove()
方法
14旭咽、線程池
池化技術(shù)
優(yōu)點(diǎn):
降低資源消耗。通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗赌厅。
提高響應(yīng)速度穷绵。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行特愿。
提高線程的可管理性仲墨。線程是稀缺資源勾缭,如果無(wú)限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源目养,還會(huì)降低系統(tǒng)的穩(wěn)定性俩由,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控混稽。
Java創(chuàng)建線程池參數(shù):核心線程數(shù)采驻、最大線程數(shù)、空線程存活時(shí)間匈勋、時(shí)間單位礼旅、任務(wù)隊(duì)列、丟棄策略洽洁、線程工廠痘系。
線程池的拒絕策略
如果當(dāng)前同時(shí)運(yùn)行的線程數(shù)量達(dá)到最大線程數(shù)量并且隊(duì)列也已經(jīng)被放滿了任務(wù)時(shí),ThreadPoolExecutor
定義一些策略:
ThreadPoolExecutor.AbortPolicy
:拋出RejectedExecutionException
來(lái)拒絕新任務(wù)的處理饿自。ThreadPoolExecutor.CallerRunsPolicy
:調(diào)用執(zhí)行自己的線程運(yùn)行任務(wù)汰翠,也就是直接在調(diào)用execute
方法的線程中運(yùn)行(run
)被拒絕的任務(wù),如果執(zhí)行程序已關(guān)閉昭雌,則會(huì)丟棄該任務(wù)复唤。因此這種策略會(huì)降低對(duì)于新任務(wù)提交速度,影響程序的整體性能烛卧。如果你的應(yīng)用程序可以承受此延遲并且你要求任何一個(gè)任務(wù)請(qǐng)求都要被執(zhí)行的話佛纫,你可以選擇這個(gè)策略。ThreadPoolExecutor.DiscardPolicy
:不處理新任務(wù)总放,直接丟棄掉呈宇。ThreadPoolExecutor.DiscardOldestPolicy
:此策略將丟棄最早的未處理的任務(wù)請(qǐng)求。
拓展:
如果服務(wù)器資源以達(dá)到可利用的極限局雄,這就意味我們要在設(shè)計(jì)策略上改變線程池的調(diào)度了甥啄,我們都知道,導(dǎo)致主線程卡死的本質(zhì)就是因?yàn)槲覀儾幌M魏我粋€(gè)任務(wù)被丟棄炬搭。換個(gè)思路蜈漓,有沒(méi)有辦法既能保證任務(wù)不被丟棄且在服務(wù)器有余力時(shí)及時(shí)處理呢?
這里提供的一種任務(wù)持久化的思路尚蝌,這里所謂的任務(wù)持久化迎变,包括但不限于:
設(shè)計(jì)一張任務(wù)表將任務(wù)存儲(chǔ)到 MySQL 數(shù)據(jù)庫(kù)中。
Redis 緩存任務(wù)飘言。
將任務(wù)提交到消息隊(duì)列中衣形。
這里以方案一為例,簡(jiǎn)單介紹一下實(shí)現(xiàn)邏輯:
實(shí)現(xiàn)
RejectedExecutionHandler
接口自定義拒絕策略,自定義拒絕策略負(fù)責(zé)將線程池暫時(shí)無(wú)法處理(此時(shí)阻塞隊(duì)列已滿)的任務(wù)入庫(kù)(保存到 MySQL 中)谆吴。注意:線程池暫時(shí)無(wú)法處理的任務(wù)會(huì)先被放在阻塞隊(duì)列中倒源,阻塞隊(duì)列滿了才會(huì)觸發(fā)拒絕策略。繼承
BlockingQueue
實(shí)現(xiàn)一個(gè)混合式阻塞隊(duì)列句狼,該隊(duì)列包含 JDK 自帶的ArrayBlockingQueue
笋熬。另外,該混合式阻塞隊(duì)列需要修改取任務(wù)處理的邏輯腻菇,也就是重寫take()
方法胳螟,取任務(wù)時(shí)優(yōu)先從數(shù)據(jù)庫(kù)中讀取最早的任務(wù),數(shù)據(jù)庫(kù)中無(wú)任務(wù)時(shí)再?gòu)?ArrayBlockingQueue
中去取任務(wù)筹吐。
常用阻塞隊(duì)列
LinkedBlockingQueue
ArrayBlockingQueue
SynchronousQueue
DelayedWorkedQueue
線程池中線程異常是復(fù)用還是銷毀糖耸?
execute 銷毀
submit 復(fù)用
核心線程數(shù)參數(shù)確定
CPU密集型 (N+1)
IO密集型(2N)
如果有資源的話 ,結(jié)合實(shí)際情況具體壓測(cè)下丘薛。
動(dòng)態(tài)線程池
https://mp.weixin.qq.com/s/9HLuPcoWmTqAeFKa1kj-_A
15嘉竟、CompletableFuture
引用:CompletableFuture從入門、踩坑洋侨、迷茫舍扰、到精通(全網(wǎng)看這一篇夠了)_completablefuture.get()坑-CSDN博客
16、AQS
17希坚、為什么 wait() 方法不定義在 Thread 中边苹?
wait()
是讓獲得對(duì)象鎖的線程實(shí)現(xiàn)等待,會(huì)自動(dòng)釋放當(dāng)前線程占有的對(duì)象鎖裁僧。每個(gè)對(duì)象(Object
)都擁有對(duì)象鎖勾给,既然要釋放當(dāng)前線程占有的對(duì)象鎖并讓其進(jìn)入 WAITING 狀態(tài),自然是要操作對(duì)應(yīng)的對(duì)象(Object
)而非當(dāng)前的線程(Thread
)锅知。
類似的問(wèn)題:為什么 sleep()
方法定義在 Thread
中?
因?yàn)?sleep()
是讓當(dāng)前線程暫停執(zhí)行脓钾,不涉及到對(duì)象類售睹,也不需要獲得對(duì)象鎖。
18可训、如何理解線程安全和不安全昌妹?
線程安全和不安全是在多線程環(huán)境下對(duì)于同一份數(shù)據(jù)的訪問(wèn)是否能夠保證其正確性和一致性的描述。
線程安全指的是在多線程環(huán)境下握截,對(duì)于同一份數(shù)據(jù)飞崖,不管有多少個(gè)線程同時(shí)訪問(wèn),都能保證這份數(shù)據(jù)的正確性和一致性谨胞。
線程不安全則表示在多線程環(huán)境下固歪,對(duì)于同一份數(shù)據(jù),多個(gè)線程同時(shí)訪問(wèn)時(shí)可能會(huì)導(dǎo)致數(shù)據(jù)混亂、錯(cuò)誤或者丟失牢裳。
19逢防、如何預(yù)防死鎖? 破壞死鎖的產(chǎn)生的必要條件即可:
破壞請(qǐng)求與保持條件:一次性申請(qǐng)所有的資源蒲讯。
破壞不剝奪條件:占用部分資源的線程進(jìn)一步申請(qǐng)其他資源時(shí)忘朝,如果申請(qǐng)不到,可以主動(dòng)釋放它占有的資源判帮。
破壞循環(huán)等待條件:靠按序申請(qǐng)資源來(lái)預(yù)防局嘁。按某一順序申請(qǐng)資源,釋放資源則反序釋放晦墙。破壞循環(huán)等待條件悦昵。
這個(gè)讓我想起了經(jīng)典的哲學(xué)家進(jìn)餐問(wèn)題:
如上有三種解法:
1、設(shè)置臨界區(qū)偎痛,一個(gè)人一次性申請(qǐng)左右兩個(gè)叉子旱捧。
2、設(shè)置信號(hào)量踩麦,最多 4人同時(shí)持有叉子枚赡。
3、奇數(shù)位申請(qǐng)順序谓谦,先左后右贫橙;偶數(shù)位先右后左。
關(guān)于多線程反粥,多考量下業(yè)務(wù)場(chǎng)景
引用目錄
CompletableFuture從入門卢肃、踩坑、迷茫才顿、到精通(全網(wǎng)看這一篇夠了)_completablefuture.get()坑-CSDN博客