阿里巴巴对Java编程【并发处理】的规约
轉(zhuǎn)載自?阿里巴巴對(duì)Java編程【并發(fā)處理】的規(guī)約
并發(fā)處理
1. 【強(qiáng)制】獲取單例對(duì)象需要保證線程安全,其中的方法也要保證線程安全。
說(shuō)明:資源驅(qū)動(dòng)類(lèi)、工具類(lèi)、單例工廠類(lèi)都需要注意。
2. 【強(qiáng)制】創(chuàng)建線程或線程池時(shí)請(qǐng)指定有意義的線程名稱(chēng),方便出錯(cuò)時(shí)回溯。
正例:
public class TimerTaskThread extends Thread {public TimerTaskThread() {super.setName("TimerTaskThread");...} } ? ?
3. 【強(qiáng)制】線程資源必須通過(guò)線程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線程。
說(shuō)明:使用線程池的好處是減少在創(chuàng)建和銷(xiāo)毀線程上所花的時(shí)間以及系統(tǒng)資源的開(kāi)銷(xiāo),解決資源不足的問(wèn)題。如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量同類(lèi)線程而導(dǎo)致消耗完內(nèi)存或者“過(guò)度切換”的問(wèn)題。
4. 【強(qiáng)制】線程池不允許使用 Executors 去創(chuàng)建,而是通過(guò) ThreadPoolExecutor 的方式,這樣的處理方式讓寫(xiě)的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。
說(shuō)明: Executors 返回的線程池對(duì)象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool :
允許的請(qǐng)求隊(duì)列長(zhǎng)度為 Integer.MAX_VALUE ,可能會(huì)堆積大量的請(qǐng)求,從而導(dǎo)致 OOM 。
2) CachedThreadPool 和 ScheduledThreadPool :
允許的創(chuàng)建線程數(shù)量為 Integer.MAX_VALUE ,可能會(huì)創(chuàng)建大量的線程,從而導(dǎo)致 OOM 。
5. 【強(qiáng)制】 SimpleDateFormat 是線程不安全的類(lèi),一般不要定義為 static 變量,如果定義為static ,必須加鎖,或者使用 DateUtils 工具類(lèi)。
正例:注意線程安全,使用 DateUtils 。亦推薦如下處理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {@ Overrideprotected DateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd");} }; ?
說(shuō)明:如果是 JDK 8 的應(yīng)用,可以使用 Instant 代替 Date , LocalDateTime 代替 Calendar ,DateTimeFormatter 代替 SimpleDateFormat ,官方給出的解釋: simple beautiful strong immutable thread - safe 。
6. 【強(qiáng)制】高并發(fā)時(shí),同步調(diào)用應(yīng)該去考量鎖的性能損耗。能用無(wú)鎖數(shù)據(jù)結(jié)構(gòu),就不要用鎖 ; 能鎖區(qū)塊,就不要鎖整個(gè)方法體 ; 能用對(duì)象鎖,就不要用類(lèi)鎖。
說(shuō)明:盡可能使加鎖的代碼塊工作量盡可能的小,避免在鎖代碼塊中調(diào)用 RPC 方法。
7. 【強(qiáng)制】對(duì)多個(gè)資源、數(shù)據(jù)庫(kù)表、對(duì)象同時(shí)加鎖時(shí),需要保持一致的加鎖順序,否則可能會(huì)造成死鎖。
說(shuō)明:線程一需要對(duì)表 A 、 B 、 C 依次全部加鎖后才可以進(jìn)行更新操作,那么線程二的加鎖順序也必須是 A 、 B 、 C ,否則可能出現(xiàn)死鎖。
8. 【強(qiáng)制】并發(fā)修改同一記錄時(shí),避免更新丟失,需要加鎖。要么在應(yīng)用層加鎖,要么在緩存加鎖,要么在數(shù)據(jù)庫(kù)層使用樂(lè)觀鎖,使用 version 作為更新依據(jù)。
說(shuō)明:如果每次訪問(wèn)沖突概率小于 20%,推薦使用樂(lè)觀鎖,否則使用悲觀鎖。樂(lè)觀鎖的重試次數(shù)不得小于 3 次。
9. 【強(qiáng)制】多線程并行處理定時(shí)任務(wù)時(shí), Timer 運(yùn)行多個(gè) TimeTask 時(shí),只要其中之一沒(méi)有捕獲拋出的異常,其它任務(wù)便會(huì)自動(dòng)終止運(yùn)行,使用 ScheduledExecutorService 則沒(méi)有這個(gè)問(wèn)題。
10. 【推薦】使用 CountDownLatch 進(jìn)行異步轉(zhuǎn)同步操作,每個(gè)線程退出前必須調(diào)用 countDown方法,線程執(zhí)行代碼注意 catch 異常,確保 countDown 方法被執(zhí)行到,避免主線程無(wú)法執(zhí)行至 await 方法,直到超時(shí)才返回結(jié)果。
說(shuō)明:注意,子線程拋出異常堆棧,不能在主線程 try - catch 到。
11. 【推薦】避免 Random 實(shí)例被多線程使用,雖然共享該實(shí)例是線程安全的,但會(huì)因競(jìng)爭(zhēng)同一seed 導(dǎo)致的性能下降。
說(shuō)明: Random 實(shí)例包括 java . util . Random 的實(shí)例或者 ?Math . random() 的方式。
正例:在 JDK 7 之后,可以直接使用 API ThreadLocalRandom ,而在 ?JDK 7 之前,需要編碼保證每個(gè)線程持有一個(gè)實(shí)例。
12. 【推薦】在并發(fā)場(chǎng)景下,通過(guò)雙重檢查鎖 (double - checked locking) 實(shí)現(xiàn)延遲初始化的優(yōu)化問(wèn)題隱患 ( 可參考 ?The " Double - Checked Locking is Broken " ?Declaration) ,推薦解決方案中較為簡(jiǎn)單一種 ( 適用于 JDK 5 及以上版本 ) ,將目標(biāo)屬性聲明為 ?volatile 型 。
反例:
class Singleton {private Helper helper = null;public Helper getHelper() {if (helper == null) synchronized(this) {if (helper == null)helper = new Helper();}return helper;} // other methods and fields... }13. 【參考】 volatile 解決多線程內(nèi)存不可見(jiàn)問(wèn)題。對(duì)于一寫(xiě)多讀,是可以解決變量同步問(wèn)題,但是如果多寫(xiě),同樣無(wú)法解決線程安全問(wèn)題。如果是 count ++操作,使用如下類(lèi)實(shí)現(xiàn):
AtomicInteger count = ?new AtomicInteger(); count . addAndGet( 1 ); ?如果是 JDK 8,推薦使用 LongAdder 對(duì)象,比 AtomicLong 性能更好 ( 減少樂(lè)觀鎖的重試次數(shù) ) 。
14. 【參考】 ?HashMap 在容量不夠進(jìn)行 resize 時(shí)由于高并發(fā)可能出現(xiàn)死鏈,導(dǎo)致 CPU 飆升,在開(kāi)發(fā)過(guò)程中可以使用其它數(shù)據(jù)結(jié)構(gòu)或加鎖來(lái)規(guī)避此風(fēng)險(xiǎn)。
15. 【參考】 ThreadLocal 無(wú)法解決共享對(duì)象的更新問(wèn)題, ThreadLocal 對(duì)象建議使用 static修飾。這個(gè)變量是針對(duì)一個(gè)線程內(nèi)所有操作共享的,所以設(shè)置為靜態(tài)變量,所有此類(lèi)實(shí)例共享此靜態(tài)變量 ,也就是說(shuō)在類(lèi)第一次被使用時(shí)裝載,只分配一塊存儲(chǔ)空間,所有此類(lèi)的對(duì)象 ( 只要是這個(gè)線程內(nèi)定義的 ) 都可以操控這個(gè)變量。
并發(fā)處理
1. 【強(qiáng)制】獲取單例對(duì)象需要保證線程安全,其中的方法也要保證線程安全。
說(shuō)明:資源驅(qū)動(dòng)類(lèi)、工具類(lèi)、單例工廠類(lèi)都需要注意。
2. 【強(qiáng)制】創(chuàng)建線程或線程池時(shí)請(qǐng)指定有意義的線程名稱(chēng),方便出錯(cuò)時(shí)回溯。
正例:
public class TimerTaskThread extends Thread {
? ?public TimerTaskThread() {
? ? ? ?super.setName("TimerTaskThread");
? ? ? ?...
? ?}
} ? ?
3. 【強(qiáng)制】線程資源必須通過(guò)線程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線程。
說(shuō)明:使用線程池的好處是減少在創(chuàng)建和銷(xiāo)毀線程上所花的時(shí)間以及系統(tǒng)資源的開(kāi)銷(xiāo),解決資源不足的問(wèn)題。如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量同類(lèi)線程而導(dǎo)致消耗完內(nèi)存或者“過(guò)度切換”的問(wèn)題。
4. 【強(qiáng)制】線程池不允許使用 Executors 去創(chuàng)建,而是通過(guò) ThreadPoolExecutor 的方式,這樣的處理方式讓寫(xiě)的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。
說(shuō)明: Executors 返回的線程池對(duì)象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool :
允許的請(qǐng)求隊(duì)列長(zhǎng)度為 Integer.MAX_VALUE ,可能會(huì)堆積大量的請(qǐng)求,從而導(dǎo)致 OOM 。
2) CachedThreadPool 和 ScheduledThreadPool :
允許的創(chuàng)建線程數(shù)量為 Integer.MAX_VALUE ,可能會(huì)創(chuàng)建大量的線程,從而導(dǎo)致 OOM 。
5. 【強(qiáng)制】 SimpleDateFormat 是線程不安全的類(lèi),一般不要定義為 static 變量,如果定義為static ,必須加鎖,或者使用 DateUtils 工具類(lèi)。
正例:注意線程安全,使用 DateUtils 。亦推薦如下處理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
? ?@ Override
? ?protected DateFormat initialValue() {
? ? ? ?return new SimpleDateFormat("yyyy-MM-dd");
? ?}
}; ?
說(shuō)明:如果是 JDK 8 的應(yīng)用,可以使用 Instant 代替 Date , LocalDateTime 代替 Calendar ,DateTimeFormatter 代替 SimpleDateFormat ,官方給出的解釋: simple beautiful strong immutable thread - safe 。
6. 【強(qiáng)制】高并發(fā)時(shí),同步調(diào)用應(yīng)該去考量鎖的性能損耗。能用無(wú)鎖數(shù)據(jù)結(jié)構(gòu),就不要用鎖 ; 能鎖區(qū)塊,就不要鎖整個(gè)方法體 ; 能用對(duì)象鎖,就不要用類(lèi)鎖。
說(shuō)明:盡可能使加鎖的代碼塊工作量盡可能的小,避免在鎖代碼塊中調(diào)用 RPC 方法。
7. 【強(qiáng)制】對(duì)多個(gè)資源、數(shù)據(jù)庫(kù)表、對(duì)象同時(shí)加鎖時(shí),需要保持一致的加鎖順序,否則可能會(huì)造成死鎖。
說(shuō)明:線程一需要對(duì)表 A 、 B 、 C 依次全部加鎖后才可以進(jìn)行更新操作,那么線程二的加鎖順序也必須是 A 、 B 、 C ,否則可能出現(xiàn)死鎖。
8. 【強(qiáng)制】并發(fā)修改同一記錄時(shí),避免更新丟失,需要加鎖。要么在應(yīng)用層加鎖,要么在緩存加鎖,要么在數(shù)據(jù)庫(kù)層使用樂(lè)觀鎖,使用 version 作為更新依據(jù)。
說(shuō)明:如果每次訪問(wèn)沖突概率小于 20%,推薦使用樂(lè)觀鎖,否則使用悲觀鎖。樂(lè)觀鎖的重試次數(shù)不得小于 3 次。
9. 【強(qiáng)制】多線程并行處理定時(shí)任務(wù)時(shí), Timer 運(yùn)行多個(gè) TimeTask 時(shí),只要其中之一沒(méi)有捕獲拋出的異常,其它任務(wù)便會(huì)自動(dòng)終止運(yùn)行,使用 ScheduledExecutorService 則沒(méi)有這個(gè)問(wèn)題。
10. 【推薦】使用 CountDownLatch 進(jìn)行異步轉(zhuǎn)同步操作,每個(gè)線程退出前必須調(diào)用 countDown方法,線程執(zhí)行代碼注意 catch 異常,確保 countDown 方法被執(zhí)行到,避免主線程無(wú)法執(zhí)行至 await 方法,直到超時(shí)才返回結(jié)果。
說(shuō)明:注意,子線程拋出異常堆棧,不能在主線程 try - catch 到。
11. 【推薦】避免 Random 實(shí)例被多線程使用,雖然共享該實(shí)例是線程安全的,但會(huì)因競(jìng)爭(zhēng)同一seed 導(dǎo)致的性能下降。
說(shuō)明: Random 實(shí)例包括 java . util . Random 的實(shí)例或者 ?Math . random() 的方式。
正例:在 JDK 7 之后,可以直接使用 API ThreadLocalRandom ,而在 ?JDK 7 之前,需要編碼保證每個(gè)線程持有一個(gè)實(shí)例。
12. 【推薦】在并發(fā)場(chǎng)景下,通過(guò)雙重檢查鎖 (double - checked locking) 實(shí)現(xiàn)延遲初始化的優(yōu)化問(wèn)題隱患 ( 可參考 ?The " Double - Checked Locking is Broken " ?Declaration) ,推薦解決方案中較為簡(jiǎn)單一種 ( 適用于 JDK 5 及以上版本 ) ,將目標(biāo)屬性聲明為 ?volatile 型 。
反例:
class Singleton {
? ?private Helper helper = null;
? ?public Helper getHelper() {
? ? ? ?if (helper == null) synchronized(this) {
? ? ? ? ? ?if (helper == null)
? ? ? ? ? ? ? ?helper = new Helper();
? ? ? ?}
? ? ? ?return helper;
? ?}
// other methods and fields...
}
13. 【參考】 volatile 解決多線程內(nèi)存不可見(jiàn)問(wèn)題。對(duì)于一寫(xiě)多讀,是可以解決變量同步問(wèn)題,但是如果多寫(xiě),同樣無(wú)法解決線程安全問(wèn)題。如果是 count ++操作,使用如下類(lèi)實(shí)現(xiàn):
AtomicInteger count = ?new AtomicInteger(); count . addAndGet( 1 ); ?如果是 JDK 8,推薦使用 LongAdder 對(duì)象,比 AtomicLong 性能更好 ( 減少樂(lè)觀鎖的重試次數(shù) ) 。
14. 【參考】 ?HashMap 在容量不夠進(jìn)行 resize 時(shí)由于高并發(fā)可能出現(xiàn)死鏈,導(dǎo)致 CPU 飆升,在開(kāi)發(fā)過(guò)程中可以使用其它數(shù)據(jù)結(jié)構(gòu)或加鎖來(lái)規(guī)避此風(fēng)險(xiǎn)。
15. 【參考】 ThreadLocal 無(wú)法解決共享對(duì)象的更新問(wèn)題, ThreadLocal 對(duì)象建議使用 static修飾。這個(gè)變量是針對(duì)一個(gè)線程內(nèi)所有操作共享的,所以設(shè)置為靜態(tài)變量,所有此類(lèi)實(shí)例共享此靜態(tài)變量 ,也就是說(shuō)在類(lèi)第一次被使用時(shí)裝載,只分配一塊存儲(chǔ)空間,所有此類(lèi)的對(duì)象 ( 只要是這個(gè)線程內(nèi)定義的 ) 都可以操控這個(gè)變量。
總結(jié)
以上是生活随笔為你收集整理的阿里巴巴对Java编程【并发处理】的规约的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 小米宣称正整改电视开机广告,提交 MAC
- 下一篇: 阿里巴巴对Java编程【控制语句】的规约