匯智動(dòng)力-Java并發(fā)編程
一、并發(fā)的級(jí)別
1、阻塞
一個(gè)線城市阻塞的,要么在其他線程釋放資源之前,當(dāng)前線程無(wú)法執(zhí)行,例如使用synchronized或者重入鎖;
2、無(wú)饑餓
如果線程是有優(yōu)先級(jí)的,那么線程調(diào)度的時(shí)候總會(huì)傾向于滿足高優(yōu)先級(jí)的線程,從而造成低優(yōu)先級(jí)的線程饑餓,而無(wú)饑餓則能夠保證饑餓不會(huì)產(chǎn)生,常見(jiàn)的手段是公平鎖,保證先來(lái)后到;
3、無(wú)障礙
無(wú)障礙的并發(fā)不會(huì)因?yàn)榕R界區(qū)導(dǎo)致線程掛起,每個(gè)線程都可以直接進(jìn)入進(jìn)入臨界區(qū),如果檢測(cè)到臟數(shù)據(jù)則對(duì)自己所做的修改立即回滾,確保數(shù)據(jù)安全;
4、無(wú)鎖
無(wú)鎖的并行都是無(wú)障礙的,在無(wú)鎖的調(diào)用中,一個(gè)典型的特點(diǎn)是包含一個(gè)無(wú)窮循環(huán),在這個(gè)循環(huán)中,線程會(huì)不斷的嘗試修改共享變量,如果沒(méi)有沖突修改成功,否則,會(huì)不斷的嘗試?yán)^續(xù)修改,無(wú)鎖能保證必然有一個(gè)線程能夠在有限步內(nèi)完成操作離開(kāi)臨界區(qū);
5、無(wú)等待
無(wú)鎖只要求有一個(gè)線程可以在有限步內(nèi)完成操作,二無(wú)等待要求所有的線程都必須在有限步內(nèi)完成;這樣就不會(huì)引起饑餓問(wèn)題;
一個(gè)典型的無(wú)等待結(jié)構(gòu)就是RCU(Read-Copy-Update)。它的基本思想是對(duì)數(shù)據(jù)的讀不加控制,所有的讀都是無(wú)等待的,但在寫數(shù)據(jù)的時(shí)候,先取得原數(shù)據(jù)的副本,接著只修改副本數(shù)據(jù),在合適的時(shí)間內(nèi)再返回;
二、jmm的一些特性
1、原子性
一個(gè)操作不可中斷,一個(gè)線程一旦開(kāi)始執(zhí)行一個(gè)操作就不會(huì)被其他線程干擾;
例如在32位機(jī)器上對(duì)int的讀寫,但是long的讀寫就不是原子性的,如果兩個(gè)線程同時(shí)對(duì)一個(gè)long數(shù)據(jù)進(jìn)行讀寫的話就有可能會(huì)造成臟數(shù)據(jù);
2、可見(jiàn)性
一個(gè)線程修改了一個(gè)共享變量的值,其他線程能否立即知道這個(gè)修改;
3、有序性
多線程的執(zhí)行可能會(huì)導(dǎo)致程序的亂序執(zhí)行,而亂序的原因則是因?yàn)闉榱吮WC指令流水線不被中斷從而對(duì)指令進(jìn)行重排;
有前后依賴性的指令指令不能被重排:
volatile變量;
解鎖必然要發(fā)生在加鎖前
傳遞性:A先于B,B先于C,則A先于C;
三、Java并行基礎(chǔ)
1、線程狀態(tài)圖及基本操作

2、新建線程
start()而不是run()
3、終止線程
stop:stop比較暴力,會(huì)直接終止線程,并且釋放這個(gè)線程所持有的鎖,可能會(huì)導(dǎo)致數(shù)據(jù)不一致性;
正確的方法應(yīng)該是在線程體里面增加一個(gè)標(biāo)志位,標(biāo)志位代表是否終止該線程,線程執(zhí)行的時(shí)候回檢查標(biāo)志位的狀態(tài),如果為true,則退出執(zhí)行;
4、線程中斷
關(guān)于線程中斷有3個(gè)方法:
public void Thread.interrupt() //中斷線程,設(shè)置中斷標(biāo)志位
public boolean Thread.isInterrupted() //判斷是否被中斷
public static boolean Thread.interrupted() //判斷是否被中斷,并清除中斷狀態(tài)
在具體應(yīng)用的時(shí)候需要像stop線程一樣,在線程執(zhí)行方法里面檢查該線程是否被中斷,如果被中斷就退出;
5、線程等待和通知
線程等待和通知通過(guò)調(diào)用obj.wait() 和 obj.notify()方法執(zhí)行,當(dāng)調(diào)用wait方法時(shí),該線程就會(huì)在這個(gè)對(duì)象上等待,直到其他線程調(diào)用該對(duì)象notify方法,obj就是多個(gè)線程之間的通信手段;
wait方法必須在同步快中調(diào)用,notify方法會(huì)在該對(duì)象的阻塞隊(duì)列中隨機(jī)選擇一個(gè)線程運(yùn)行;
wait方法和sleep方法一樣 都可以讓線程等待一段時(shí)間,除了wait可以被喚醒,sleep需要被打斷,另外一個(gè)主要的區(qū)別就是wait方法會(huì)釋放目標(biāo)對(duì)象的鎖,而sleep方法則不會(huì);
6、suspend和resume
suspend和resume方法已被廢棄,因?yàn)閟uspend在掛起線程的同時(shí)并不會(huì)釋放任何鎖資源,從而導(dǎo)致其他線程也無(wú)法執(zhí)行,直到調(diào)用了resume方法,被掛起的程序才能被訪問(wèn);
7、join和yield
join表示無(wú)限等待,他會(huì)一直阻塞當(dāng)前線程,直到目標(biāo)線程執(zhí)行完畢;
yield會(huì)讓出cpu,然后就想爭(zhēng)奪cpu;
10、volatile與jmm
volatile關(guān)鍵字保證共享數(shù)據(jù)的可見(jiàn)性,線程會(huì)在釋放鎖之前把數(shù)據(jù)寫回主存,保證其對(duì)其他線程可見(jiàn);但是volatile只是保證可見(jiàn)性,并不能保證互斥性,而synchronized既可以保證可見(jiàn)性,又能保證互斥性;
11、守護(hù)線程
系統(tǒng)的守護(hù)者,在后臺(tái)默默執(zhí)行一些系統(tǒng)服務(wù),如垃圾回收線程、jit線程;與之相對(duì)的是用戶線程,用戶線程是系統(tǒng)的工作線程,如果用戶線程全部結(jié)束,那這個(gè)程序也就無(wú)事可做了,應(yīng)用程序就會(huì)退出;
12、線程優(yōu)先級(jí)
在Java中,使用1到10表示線程的優(yōu)先級(jí),數(shù)字越大,優(yōu)先級(jí)越高;
13、synchronized關(guān)鍵字用法
制指定加鎖對(duì)象
直接作用于實(shí)例方法,進(jìn)入同步代碼的時(shí)候會(huì)獲得當(dāng)前實(shí)例的鎖;
直接作用于靜態(tài)方法:相對(duì)于當(dāng)前類加鎖,進(jìn)入同步代碼的時(shí)候會(huì)獲得當(dāng)前類的鎖;
14、Java集合的線程安全性
線程安全的Java集合:vector,stack,enumeration,hashtable,還有并發(fā)包里面的一些類:CopyOnWriteArrayList, ConcurrentHashMap,CopyOnWriteArraySet。
四、Java并發(fā)包
1、synchronized的功能擴(kuò)展:重入鎖(ReentrantLock)
重入鎖性能要比synchronized好,控制力度比較小,而且使用起來(lái)也更靈活;
重入鎖線程在等待的時(shí)候可以被中斷,而synchronized要么獲得鎖繼續(xù)執(zhí)行,要么保持等待;
重入鎖線程在等待的時(shí)候可以限時(shí)等待;
重入鎖提供了公平鎖和非公平鎖兩種模式;
2、Condition條件
wait和notify方法和synchronized關(guān)鍵字合作使用,而condition是和重入鎖合作使用通過(guò)lock.newCondition()方法可以生成一個(gè)與重入鎖綁定的Condition對(duì)象;
Condition提供了一些基本方法:
await:是當(dāng)期線程等待,并釋放當(dāng)前鎖,當(dāng)其他線程使用signal或者signalAll時(shí),線程會(huì)重新獲得鎖并繼續(xù)執(zhí)行;
awaitUninterruptibly與await基本相同,但它不會(huì)再等待過(guò)程中響應(yīng)中斷;
signal喚醒一個(gè)線程
signalAll方法喚醒所有等待線程;
3、Semaphore信號(hào)量:允許多個(gè)線程同時(shí)訪問(wèn)
acquire申請(qǐng)?jiān)S可,若無(wú)法獲得,則線程等待直到有一個(gè)線城釋放許可或當(dāng)前線程被中斷;
acquireUninterruptibly和acquire類似,不過(guò)不響應(yīng)中斷;
release釋放許可
4、ReadWriteLock讀寫鎖
讀寫分離鎖,如果使用synchronized或者可重入鎖,線程的所有讀之間,讀與寫之間,寫與寫之間都是串行操作,但讀與度之不會(huì)造成數(shù)據(jù)的破壞,這也就是讀寫鎖所做的事情;
5、倒計(jì)時(shí)器CountDownLatch
6、循環(huán) 柵欄CyclicBarrier
7、線程池
Executor提供了各種各樣的線程池:
newFixedThreadPool:固定數(shù)量線程的線程池,當(dāng)任務(wù)超出線程數(shù)量時(shí),暫存在阻塞隊(duì)列中;
newSingleExecutor:返回一個(gè)只有一個(gè)線程的線程池,多于一個(gè)任務(wù)的話,保存在阻塞隊(duì)列中;
newCachedThreadPool:返回一個(gè)可根據(jù)實(shí)際情況調(diào)整線程數(shù)量的線程池,但是可以設(shè)置線程池的最大線程數(shù)量,線程存活時(shí)間等;
newSingleThreadScheduleExecutor:返回一個(gè)ScheduledExecutorService對(duì)象,線程池大小為1,有定時(shí)執(zhí)行任務(wù)的功能;
newScheduleThreadPool:返回一個(gè)ScheduledExecutorService對(duì)象,但線程池的線程數(shù)量可以指定;
當(dāng)線程池超過(guò)負(fù)載的時(shí)候(線程池用完,等待隊(duì)列用完?。?,jdk有幾種內(nèi)置的策略:
AbortPolicy:直接拋出異常,阻止系統(tǒng)正常工作
CallerRunsPolicy:只要線程池未關(guān)閉,直接在線程池中運(yùn)行該任務(wù)
DiscardOledestPolicy:丟棄一個(gè)最老的請(qǐng)求
DiscardPolicy:直接丟棄,不與任何處理;
8、Fork/Join框架:分而治之
類似于分治思想,F(xiàn)ork階段用來(lái)把任務(wù)分割成多個(gè)子任務(wù),Join階段用來(lái)收集結(jié)果
五、鎖的優(yōu)化及注意事項(xiàng)
1、提高鎖性能的幾點(diǎn)建議
減小鎖持有時(shí)間、減小鎖粒度、讀寫分離鎖替換獨(dú)占鎖、鎖分離(不同的操作不同的鎖)、鎖粗化(鎖的粒度太小也不好,會(huì)造成線程反復(fù)的申請(qǐng)鎖)
2、jdk提供的鎖策略
鎖偏向、輕量級(jí)鎖、自旋鎖、鎖消除
3、人手一支筆:ThreadLocal
ThreadLocal把共享變量編程每個(gè)線程的內(nèi)部變量,只有當(dāng)前線程訪問(wèn);
4、無(wú)鎖CAS
CAS的算法過(guò)程是這樣的:它包含3個(gè)參數(shù)CAS(V,E,N),V表示要更新的變量,E表示預(yù)期的值,N表示新值。僅當(dāng)V等于E時(shí),才把V更新為N,如果不等,表示有其他的線程做了更新,則當(dāng)前線程什么都不做。最后,CAS返回當(dāng)前V的真實(shí)值;
但是CAS也有可能出現(xiàn)問(wèn)題,即如果一個(gè)線程改了值,但是又被另一個(gè)線程改了回去,則當(dāng)前線程會(huì)認(rèn)為其值沒(méi)有變化過(guò)進(jìn)而對(duì)其進(jìn)行更新,在有的應(yīng)用可能會(huì)出現(xiàn)問(wèn)題,可以在增加一個(gè)時(shí)間戳參數(shù)來(lái)保證。

