基本概念
并發(fā):同時(shí)擁有兩個(gè)或多個(gè)線程献汗,如果程序在單核處理器上運(yùn)行暇赤,多個(gè)線程將交替的換入或者換出內(nèi)存耙蔑,這些線程是同時(shí)“存在”的,每個(gè)線程都處于執(zhí)行過程中的某個(gè)狀態(tài)疟赊,如果運(yùn)行在多核處理器上郊供,此時(shí),程序中的每個(gè)線程都將分配到一個(gè)處理器核上近哟,因此可以同時(shí)運(yùn)行驮审。(多個(gè)線程操作相同的資源,保證線程安全吉执,合理使用資源)
高并發(fā):高并發(fā)(High Concurrency)是互聯(lián)網(wǎng)分布式系統(tǒng)架構(gòu)設(shè)計(jì)中必須考慮的因素之一疯淫,它通常是指,通過設(shè)計(jì)保證系統(tǒng)能夠同時(shí)并行處理很多請(qǐng)求戳玫。(服務(wù)能同時(shí)處理很多請(qǐng)求熙掺,提高程序性能)
Java內(nèi)存模型(Java Memory Model,JMM)
Java內(nèi)存模型-同步八種操作
lock(鎖定):作用與主內(nèi)存的變量咕宿,把一個(gè)變量表示為一條線程獨(dú)占狀態(tài)币绩。
unlock(解鎖):作用與主內(nèi)存的變量,把一個(gè)鎖定狀態(tài)的變量釋放出來府阀,釋放后的變量才可以被其他線程鎖定缆镣。
read(讀取):作用與主內(nèi)存的變量试浙,把一個(gè)變量值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中董瞻,以便隨后的load動(dòng)作使用。
load(載入):作用于工作內(nèi)存的變量川队,它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中力细。
use(使用):作用于工作內(nèi)存的變量,把工作內(nèi)存中的一個(gè)變量值傳遞給執(zhí)行引擎固额。
assign(賦值):作用于工作內(nèi)存的變量,它把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi)存的變量煞聪。
store(存儲(chǔ)):作用于工作內(nèi)存的變量斗躏,把工作內(nèi)存中的一個(gè)變量的值傳送到主內(nèi)存中,以便隨后的write操作昔脯。
write(寫入):作用與主內(nèi)存的變量啄糙,它把store操作從工作內(nèi)存中一個(gè)變量的值傳送到主內(nèi)存的變量中。
線程安全性
當(dāng)多個(gè)線程訪問某個(gè)類時(shí)云稚,不管運(yùn)行時(shí)環(huán)境采用何種調(diào)度方式或者這些進(jìn)程將如何交替執(zhí)行隧饼,并且在主調(diào)代碼中不需要任何額外的同步或協(xié)同,這個(gè)類都能表現(xiàn)出正確的行為静陈,那么就成這個(gè)類是線程安全的绊茧。
原子性:提供了互斥訪問,同一時(shí)刻只能有一個(gè)線程來對(duì)它進(jìn)行操作
可見性:一個(gè)線程對(duì)于主內(nèi)存的修改可以及時(shí)的被其他線程觀察到
有序性:一個(gè)線程觀察其他線程中的指令執(zhí)行順序嫁佳,由于指令重排序的存在恬试,該觀察結(jié)果一般雜亂無序。
原子性對(duì)比
synchronized:不可中斷鎖七兜,適合競(jìng)爭(zhēng)不激烈,可讀性好。
Lock:可中斷鎖懂衩,多樣化同步,競(jìng)爭(zhēng)激烈時(shí)能維持常態(tài)金踪。
Atomic:競(jìng)爭(zhēng)激烈時(shí)能維持常態(tài)浊洞,比Lock性能好;只能同步一個(gè)值胡岔。
可見性
導(dǎo)致共享變量在線程間不可見的原因:
- 線程交叉執(zhí)行
- 重排序結(jié)合線程交叉執(zhí)行
- 共享變量更新后的值沒有在工作內(nèi)存與主存間及時(shí)更新
JMM關(guān)于synchronized的兩條規(guī)定:
- 線程解鎖前沛申,必須把共享變量的最新值刷新到主內(nèi)存。
- 線程加鎖時(shí)姐军,將清空工作內(nèi)存中共享變量的值铁材,從而使用共享變量時(shí)需要從主內(nèi)存中重新讀取最新的值(加鎖與解鎖是同一把鎖)
可見性-volatile
通過加入內(nèi)存屏障和禁止重排序優(yōu)化來實(shí)現(xiàn)
- 對(duì)volatile變量寫操作時(shí),會(huì)在寫操作后加入一條store屏障指令奕锌,將本地內(nèi)存中的共享變量值刷新到主內(nèi)存著觉。
- 對(duì)volatile變量讀操作時(shí),會(huì)在讀操作前加入一條load屏障指令惊暴,從主內(nèi)存中讀取共享變量饼丘。
// volatile 適用場(chǎng)景
volatile boolean inited = false;
// 線程1:
context = loadContext();
inited = true;
// 線程2:
while (!inited){
sleep();
}
doSomethingWithConfig(context);
有序性
Java內(nèi)存模型中,允許編譯器和處理器對(duì)指令進(jìn)行重排序辽话,但是重排序過程不會(huì)影響到單線程程序的執(zhí)行肄鸽,卻會(huì)影響到多線程并發(fā)執(zhí)行的正確性。
有序性-happens-before原則
- 程序次序規(guī)則:一個(gè)線程內(nèi)油啤,按照代碼順序典徘,書寫在前面的操作先行發(fā)生于書寫在后面的操作(指令重排序不影響單線程內(nèi)的結(jié)果)。
- 鎖定規(guī)則:一個(gè)unlock操作先行發(fā)生于后面對(duì)同一個(gè)鎖的lock操作益咬。
- volatile變量規(guī)則:對(duì)一個(gè)變量的寫操作先行發(fā)生于后面對(duì)這個(gè)變量的讀操作逮诲。
- 傳遞規(guī)則:如果操作A先行發(fā)生于操作B,而操作B又先行發(fā)生于操作C幽告,則可以得出操作A先行發(fā)生于操作C梅鹦。
- 線程啟動(dòng)規(guī)則:Thread對(duì)象的start()方法先行發(fā)生于此線程的每一個(gè)動(dòng)作
- 線程中斷規(guī)則:對(duì)線程interrupt()方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測(cè)到中斷事件的發(fā)生
- 線程終結(jié)規(guī)則:線程中所有的操作都先行發(fā)生于線程的終止檢測(cè),我們可以通過Thread.join()方法結(jié)束冗锁、Thread.isAlive()的返回值手段檢測(cè)到線程已經(jīng)終止執(zhí)行
- 對(duì)象終結(jié)規(guī)則:一個(gè)對(duì)象的初始化完成先行發(fā)生于他的finalize()方法的開始
發(fā)布對(duì)象
使一個(gè)對(duì)象能夠被當(dāng)前范圍之外的代碼所使用齐唆。
在我們的日常開發(fā)中,我們經(jīng)常要發(fā)布一些對(duì)象冻河,比如通過類的非私有方法返回對(duì)象的引用箍邮,或者通過公有靜態(tài)變量發(fā)布對(duì)象茉帅。
對(duì)象逸出
一種錯(cuò)誤的發(fā)布。當(dāng)一個(gè)對(duì)象還沒有構(gòu)造完成時(shí)媒殉,就使它被其他線程所見担敌。
不可變對(duì)象
有一種對(duì)象只要它發(fā)布了就是安全的,它就是不可變對(duì)象廷蓉。一個(gè)不可變對(duì)象需要滿足的條件:
對(duì)象創(chuàng)建以后其狀態(tài)不能修改
對(duì)象所有域都是final類型
對(duì)象是正確創(chuàng)建的(在對(duì)象創(chuàng)建期間全封,this引用沒有逸出)
final關(guān)鍵字
final關(guān)鍵字可以修飾類、修飾方法桃犬、修飾變量
修飾類:類不能被集成刹悴。
基礎(chǔ)類型的包裝類都是final類型的類。final類中的成員變量可以根據(jù)需要設(shè)置為final攒暇,但是要注意的是土匀,final類中的所有成員方法都會(huì)被隱式的指定為final方法
修飾方法:
(1)把方法鎖定,以防任何繼承類修改它的含義
(2)效率:在早期的java實(shí)現(xiàn)版本中形用,會(huì)將final方法轉(zhuǎn)為內(nèi)嵌調(diào)用就轧。但是如果方法過于龐大,可能看不見效果田度。一個(gè)private方法會(huì)被隱式的指定為final方法
修飾變量:
基本數(shù)據(jù)類型變量妒御,在初始化之后,它的值就不能被修改了镇饺。如果是引用類型變量乎莉,在它初始化之后便不能再指向另外的對(duì)象。
死鎖-必要條件
- 互斥條件:進(jìn)程對(duì)鎖分配的資源進(jìn)行排他性使用
- 請(qǐng)求和保持條件:線程已經(jīng)保持了一個(gè)資源奸笤,但是又提出了其他請(qǐng)求惋啃,而該資源已被其他線程占用
- 不剝奪條件:在使用時(shí)不能被剝奪,只能自己用完釋放
- 環(huán)路等待條件:資源調(diào)用是一個(gè)環(huán)形的鏈
多線程并發(fā)最佳實(shí)踐
- 使用本地變量
- 使用不可變類
- 最小化鎖的作用域范圍:S=1/(1-a+a/n)
- 使用線程池的Executor监右,而不是直接new Thread執(zhí)行
- 寧可使用同步也不要使用線程的wait和notify
- 使用BlockingQueue實(shí)現(xiàn)生產(chǎn)-消費(fèi)模式
- 使用并發(fā)集合而不是加了鎖的同步集合
- 使用Semaphore創(chuàng)建有界的訪問
- 寧可使用同步代碼塊也不使用同步的方法
- 避免使用靜態(tài)變量
限流算法
計(jì)數(shù)器法
滑動(dòng)窗口
漏桶
令牌桶
高可用的一些手段
- 任務(wù)調(diào)度系統(tǒng)分布式:elastic-job+zookeeper
- 主備切換:apache curator+zookeeper分布式鎖實(shí)現(xiàn)
- 監(jiān)控報(bào)警機(jī)制