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

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

