Java 7 并发编程指南
原文是發(fā)表在并發(fā)編程網(wǎng)上翻譯后的 《Java 7 并發(fā)編程指南》,這里對其中的目錄做個(gè)更加詳細(xì)的描述,并且寫出了重點(diǎn)說明,方便日后快速查閱。建議仔細(xì)查看每節(jié)的代碼實(shí)現(xiàn),非常具有參考價(jià)值。可以直接點(diǎn)擊標(biāo)題,查看原文。
原文目錄地址:http://ifeve.com/java-7-concurrency-cookbook/
代碼實(shí)現(xiàn):https://github.com/Wang-Jun-Chao/java-concurrency
目錄
前言
第一章: 線程管理
介紹
本節(jié)主要介紹線程基本的操作。
線程的創(chuàng)建和運(yùn)行
在Java中,我們有2個(gè)方式創(chuàng)建線程:
- 通過直接繼承thread類,然后覆蓋run()方法。
- 構(gòu)建一個(gè)實(shí)現(xiàn)Runnable接口的類, 然后創(chuàng)建一個(gè)thread類對象并傳遞Runnable對象作為構(gòu)造參數(shù)
之后調(diào)用這個(gè)線程對象的start()對象即可運(yùn)行。
獲取和設(shè)置線程信息
Thread類的對象中保存了一些屬性信息能夠幫助我們來辨別每一個(gè)線程,知道它的狀態(tài),調(diào)整控制其優(yōu)先級。 這些屬性是:
- ID: 每個(gè)線程的獨(dú)特標(biāo)識。
- Name: 線程的名稱。
- Priority: 線程對象的優(yōu)先級。優(yōu)先級別在1-10之間,1是最低級,10是最高級。不建議改變它們的優(yōu)先級,但是你想的話也是可以的。
- Status: 線程的狀態(tài)。在Java中,線程只能有這6種中的一種狀態(tài): new, runnable, blocked, waiting, time waiting, 或 terminated.
線程的中斷
Java提供中斷機(jī)制來通知線程表明我們想要結(jié)束它。中斷機(jī)制的特性是線程需要檢查是否被中斷,而且還可以決定是否響應(yīng)結(jié)束的請求。所以,線程可以忽略中斷請求并且繼續(xù)運(yùn)行。如果需要中斷線程,只需要調(diào)用線程對象的interrupt()方法即可,線程通過isInterrupted()方法來檢測自己是否被中斷。比如,你可以像例子中一樣,輪詢檢測自己是否被中斷,如果被中斷了就直接return。
操作線程的中斷機(jī)制
我們可以選擇使用拋出InterruptedException異常的方式來停止線程。通常它可以由并發(fā)API來拋出比如sleep()方法
線程的睡眠和恢復(fù)
當(dāng)你調(diào)用sleep()方法, Thread 離開CPU并在一段時(shí)間內(nèi)停止運(yùn)行。在這段時(shí)間內(nèi),它是不消耗CPU時(shí)間的,使得CPU可以執(zhí)行其他任務(wù)。
當(dāng) Thread 是睡眠并且處于中斷狀態(tài)(比如調(diào)用了線程的interrupt()方法)的時(shí)候,sleep方法會立刻拋出InterruptedException異常并不會一直等到睡眠時(shí)間過去。
Java 并發(fā) API 有另一種方法能讓線程對象離開 CPU。它是 yield() 方法, 它向JVM表示線程對象可以讓CPU執(zhí)行其他任務(wù)。JVM 不保證它會遵守請求。通常,它只是用來試調(diào)的。
等待線程的終結(jié)
當(dāng)線程調(diào)用某個(gè)其他線程的join()方法時(shí),它會暫停當(dāng)前線程,直到被調(diào)用join()方法的其他線程執(zhí)行完成。
守護(hù)線程的創(chuàng)建和運(yùn)行
調(diào)用線程對象的setDaemon(true);來使其成為一個(gè)守護(hù)線程。
處理線程的不受控制異常
Java里有2種異常:
- 檢查異常(Checked exceptions): 這些異常必須強(qiáng)制捕獲它們或在一個(gè)方法里的throws子句中。 例如, IOException 或者ClassNotFoundException。
- 未檢查異常(Unchecked exceptions): 這些異常不用強(qiáng)制捕獲它們。例如, NumberFormatException。當(dāng)一個(gè)非檢查異常被拋出,默認(rèn)的行為是在控制臺寫下stack trace并退出程序。
我們可以使用線程對象的setUncaughtExceptionHandler方法來設(shè)置未檢測異常的回調(diào)函數(shù)。這個(gè)回調(diào)函數(shù)以Thread 對象和 Exception 作為參數(shù)。
此外調(diào)用Thread.setDefaultUncaughtExceptionHandler()可以為應(yīng)用里的所有線程對象建立異常 handler 。
當(dāng)一個(gè)未捕捉的異常在線程里被拋出,JVM會尋找此異常的3種可能潛在的處理者(handler)。首先, 它尋找這個(gè)未捕捉的線程對象的異常handle,如我們在在這個(gè)指南中學(xué)習(xí)的。如果這個(gè)handle 不存在,那么JVM會在線程對象的ThreadGroup里尋找非捕捉異常的handler,如在處理線程組內(nèi)的不受控制異常里介紹的那樣(下面的第12節(jié))。如果此方法也不存在,那么 JVM 會尋找默認(rèn)非捕捉異常handle,就是調(diào)用Thread.setDefaultUncaughtExceptionHandler()設(shè)置的那個(gè)handle。如果沒有一個(gè)handler存在, 那么 JVM會把異常的 stack trace 寫入操控臺并結(jié)束任務(wù)。
使用本地線程變量
使用ThreadLocal來存儲線程局部變量。它的使用類似于這種:
線程組
Java 提供 ThreadGroup 類來組織線程。 ThreadGroup 對象可以由 Thread 對象組成和由另外的 ThreadGroup 對象組成,生成線程樹結(jié)構(gòu)。
ThreadGroup 類儲存線程對象和其他有關(guān)聯(lián)的 ThreadGroup 對象,所以它可以訪問他們的所有信息 (例如,狀態(tài)) 和全部成員的操作表現(xiàn) (例如,中斷)。
處理線程組內(nèi)的不受控制異常
重寫ThreadGroup的uncaughtException方法來為線程組設(shè)置未捕獲異常的回調(diào)方法
用線程工廠創(chuàng)建線程
實(shí)現(xiàn)ThreadFactory接口來使用線程工廠創(chuàng)建線程,你需要知道使用線程工廠創(chuàng)建線程有什么優(yōu)勢,一般來說它具有下面這些優(yōu)勢:
- 更簡單的改變了類的對象創(chuàng)建或者說創(chuàng)建這些對象的方式。
- 更簡單的為了限制的資源限制了對象的創(chuàng)建。 例如, 我們只new一個(gè)此類型的對象。
- 更簡單的生成創(chuàng)建對象的統(tǒng)計(jì)數(shù)據(jù)。
第二章 : 基本線程同步
介紹
在并發(fā)應(yīng)用程序中,多個(gè)線程讀或?qū)懴嗤臄?shù)據(jù)或訪問同一文件或數(shù)據(jù)庫連接這是正常的。這些共享資源會引發(fā)錯(cuò)誤或數(shù)據(jù)不一致的情況,我們必須通過一些機(jī)制來避免這些錯(cuò)誤。
同步方法
synchronized關(guān)鍵字基本使用
在同步的類里安排獨(dú)立屬性
當(dāng)你使用synchronized關(guān)鍵字來保護(hù)代碼塊時(shí),你必須通過一個(gè)對象的引用作為參數(shù)。通常,你將會使用this關(guān)鍵字來引用執(zhí)行該方法的對象,但是你也可以使用其他對象引用。通常情況下,這些對象被創(chuàng)建只有這個(gè)目的。比如,你在一個(gè)類中有被多個(gè)線程共享的兩個(gè)獨(dú)立屬性。你必須同步訪問每個(gè)變量,如果有一個(gè)線程訪問一個(gè)屬性和另一個(gè)線程在同一時(shí)刻訪問另一個(gè)屬性,這是沒有問題的。
在同步代碼中使用條件
涉及wait(),notify(),和notifyAll() 方法。
一個(gè)線程可以在synchronized代碼塊中調(diào)用wait()方法。如果在synchronized代碼塊外部調(diào)用wait()方法,JVM會拋出IllegalMonitorStateException異常。當(dāng)線程調(diào)用wait()方法,JVM讓這個(gè)線程睡眠,并且釋放控制 synchronized代碼塊的對象,這樣,雖然它正在執(zhí)行但允許其他線程執(zhí)行由該對象保護(hù)的其他synchronized代碼塊。為了喚醒線程,你必 須在由相同對象保護(hù)的synchronized代碼塊中調(diào)用notify()或notifyAll()方法。
通常我們會將wait()寫在一個(gè)while()循環(huán)中,以在喚醒之后再次檢測是否滿足條件,滿足則繼續(xù)執(zhí)行,否則再次進(jìn)入wait(),因?yàn)閣ait()有的時(shí)候會被錯(cuò)誤的喚醒。。
使用Lock來同步代碼塊
Java提供另外的機(jī)制用來同步代碼塊。它比synchronized關(guān)鍵字更加強(qiáng)大、靈活。它是基于Lock接口和實(shí)現(xiàn)它的類(如ReentrantLock)。這種機(jī)制有如下優(yōu)勢:
- 它允許以一種更靈活的方式來構(gòu)建synchronized塊。使用synchronized關(guān)鍵字,你必須以結(jié)構(gòu)化方式得到釋放synchronized代碼塊的控制權(quán)。Lock接口允許你獲得更復(fù)雜的結(jié)構(gòu)來實(shí)現(xiàn)你的臨界區(qū)。
- Lock 接口比synchronized關(guān)鍵字提供更多額外的功能。新功能之一是實(shí)現(xiàn)的tryLock()方法。這種方法試圖獲取鎖的控制權(quán)并且如果它不能獲取該鎖是因?yàn)槠渌€程在使用這個(gè)鎖,它將返回這個(gè)鎖。使用synchronized關(guān)鍵字的時(shí)候,當(dāng)線程A試圖執(zhí)行synchronized代碼塊,如果線程B正在執(zhí)行它,那么線程A將阻塞直到線程B執(zhí)行完synchronized代碼塊。而使用鎖,你可以執(zhí)行tryLock()方法,這個(gè)方法返回一個(gè) Boolean值表示是否有其他線程正在運(yùn)行這個(gè)鎖所保護(hù)的代碼。
- 當(dāng)有多個(gè)讀者和一個(gè)寫者時(shí),Lock接口允許讀寫操作分離。
- Lock接口比synchronized關(guān)鍵字提供更好的性能。
使用讀/寫鎖來同步數(shù)據(jù)訪問
鎖所提供的最重要的改進(jìn)之一就是ReadWriteLock接口和唯一 一個(gè)實(shí)現(xiàn)它的ReentrantReadWriteLock類。這個(gè)類提供兩把鎖,一把用于讀操作和一把用于寫操作。同時(shí)可以有多個(gè)線程執(zhí)行讀操作。當(dāng)一個(gè)線程正在執(zhí)行一個(gè)寫操作,不可能有任何線程執(zhí)行讀操作。當(dāng)一個(gè)線程正在執(zhí)行一個(gè)讀操作,也不可能有任何線程執(zhí)行寫操作。總結(jié)來說就是:讀讀不互斥,讀寫,寫寫都是互斥的。
修改Lock的公平性
ReentrantLock類和 ReentrantReadWriteLock類的構(gòu)造器中,允許一個(gè)名為fair的boolean類型參數(shù),它允許你來控制這些類的行為。默認(rèn)值為 false,這將啟用非公平模式。在這個(gè)模式中,當(dāng)有多個(gè)線程正在等待一把鎖(ReentrantLock或者 ReentrantReadWriteLock),這個(gè)鎖必須選擇它們中間的一個(gè)來獲得進(jìn)入臨界區(qū),選擇任意一個(gè)是沒有任何標(biāo)準(zhǔn)的。true值將開啟公平 模式。在這個(gè)模式中,當(dāng)有多個(gè)線程正在等待一把鎖(ReentrantLock或者ReentrantReadWriteLock),這個(gè)鎖必須選擇它們 中間的一個(gè)來獲得進(jìn)入臨界區(qū),它將選擇等待時(shí)間最長的線程。
在Lock中使用多條件
涉及await()、signal()和signallAll();
這有點(diǎn)像前面的第4節(jié)在同步代碼中使用條件。但是await()、signal()和signallAll()是配合Lock來進(jìn)行使用的。而wait(),notify(),和notifyAll() 是配合synchronized來使用的。并且前者可以實(shí)現(xiàn)鎖上的多個(gè)條件的等待。可以查看該章節(jié)示例代碼中的Buffer類來查看如何使用。
第三章: 線程同步工具
介紹
在第二章,基本的線程同步中,我們學(xué)會了以下2個(gè)同步機(jī)制:
- 關(guān)鍵詞同步(synchronized)
- Lock接口和它的實(shí)現(xiàn)類們:ReentrantLock, ReentrantReadWriteLock.ReadLock, 和 ReentrantReadWriteLock.WriteLock
在此章節(jié),我們將學(xué)習(xí)怎樣使用高等級的機(jī)制來達(dá)到多線程的同步。這些高等級機(jī)制有:
- Semaphores: 控制訪問多個(gè)共享資源的計(jì)數(shù)器。此機(jī)制是并發(fā)編程的最基本的工具之一,而且大部分編程語言都會提供此機(jī)制。
- CountDownLatch: CountDownLatch 類是Java語言提供的一個(gè)機(jī)制,它允許線程等待多個(gè)操作的完結(jié)。
- CyclicBarrier: CyclicBarrier 類是又一個(gè)java語言提供的機(jī)制,它允許多個(gè)線程在同一個(gè)點(diǎn)同步。
- Phaser: Phaser類是又一個(gè)java語言提供的機(jī)制,它控制并發(fā)任務(wù)分成段落來執(zhí)行。全部的線程在繼續(xù)執(zhí)行下一個(gè)段之前必須等到之前的段執(zhí)行結(jié)束。這是Java 7 API的一個(gè)新特性。
- Exchanger: Exchanger類也是java語言提供的又一個(gè)機(jī)制,它提供2個(gè)線程間的數(shù)據(jù)交換點(diǎn)。
控制并發(fā)訪問一個(gè)資源
使用Semaphore(信號量)類來實(shí)現(xiàn)一種比較特殊的semaphores種類,稱為binary semaphores。
控制并發(fā)訪問多個(gè)資源
使用semaphores來保護(hù)多個(gè)資源的副本,也就是說當(dāng)你有一個(gè)代碼片段可以同時(shí)被多個(gè)線程執(zhí)行。關(guān)于這一點(diǎn)可以查看該章節(jié)的示例代碼,它很好的說明了一個(gè)使用它的場景。信號量也可以用來控制并發(fā)線程數(shù)量。
等待多個(gè)并發(fā)事件完成
CountDownLatch 類的基本使用方法。有一個(gè)線程調(diào)用CountDownLatch對象的await()方法等待,等待其他線程完成必須在await()方法之后的之前完成的任務(wù)。
在一個(gè)相同點(diǎn)同步任務(wù)
CyclicBarrier 類有一個(gè)內(nèi)部計(jì)數(shù)器控制到達(dá)同步點(diǎn)的線程數(shù)量。每次線程到達(dá)同步點(diǎn),它調(diào)用 await() 方法告知 CyclicBarrier 對象到達(dá)同步點(diǎn)了。CyclicBarrier 把線程放入睡眠狀態(tài)直到全部的線程都到達(dá)他們的同步點(diǎn)。
當(dāng)全部的線程都到達(dá)他們的同步點(diǎn),CyclicBarrier 對象叫醒全部正在 await() 方法中等待的線程們,然后,選擇性的,為CyclicBarrier的構(gòu)造函數(shù) 傳遞的 Runnable 對象(例子里,是 Grouper 對象)創(chuàng)建新的線程執(zhí)行外加任務(wù)。
運(yùn)行并發(fā)階段性任務(wù)
Phaser類的使用,可以參考這里來加深對Phaser的使用。
控制并發(fā)階段性任務(wù)的改變
同上節(jié),在參考的文章中有說明。
在并發(fā)任務(wù)間交換數(shù)據(jù)
Exchange類的使用,2個(gè)線程間定義同步點(diǎn),當(dāng)2個(gè)線程到達(dá)這個(gè)點(diǎn),他們相互交換數(shù)據(jù)類型,使用第一個(gè)線程的數(shù)據(jù)類型變成第二個(gè)的,然后第二個(gè)線程的數(shù)據(jù)類型變成第一個(gè)的。
第四章: 線程執(zhí)行者
介紹
提出Executor framework概念,這有點(diǎn)像我們之前提到的線程工廠,實(shí)際上這些線程執(zhí)行者框架就是使用線程工廠來創(chuàng)建線程的。這里給出Executor框架類圖創(chuàng)建一個(gè)線程執(zhí)行者
ThreadPoolExecutor的基本使用,execute提交任務(wù),shutdown停止線程執(zhí)行者。創(chuàng)建一個(gè)大小固定的線程執(zhí)行者
上一節(jié)我們使用newCachedThreadPool()來創(chuàng)建ThreadPoolExecutor對象,它的線程池為無限大。但是在這里我們需要使用newFixedThreadPool來創(chuàng)建線程池大小固定的ThreadPoolExecutor對象,定長線程池的大小最好根據(jù)系統(tǒng)資源進(jìn)行設(shè)置。如Runtime.getRuntime().availableProcessors()。執(zhí)行者執(zhí)行返回結(jié)果的任務(wù)
Executor framework還可以并發(fā)執(zhí)行返回結(jié)果的任務(wù),這個(gè)時(shí)候我們使用的是submit函數(shù)提交任務(wù),而不是execute。submit()函數(shù)將會返回一個(gè)Future對象,使用這個(gè)對象的get方法,你將會獲得執(zhí)行結(jié)果,不止如此Future還提供了其他獲得任務(wù)執(zhí)行信息和控制任務(wù)的方法,它使你不至于將任務(wù)交給執(zhí)行器之后對任務(wù)狀態(tài)一無所知。運(yùn)行多個(gè)任務(wù)并處理第一個(gè)結(jié)果
如果你不需要像前面一樣,需要所有提交的任務(wù)的結(jié)果,而是只需要最先完成的任務(wù)的結(jié)果,你可以使用執(zhí)行器的invokeAny()方法。該方法接收一個(gè)任務(wù)列表作為參數(shù),返回最先執(zhí)行完成的任務(wù)的結(jié)果。運(yùn)行多個(gè)任務(wù)并處理所有的結(jié)果
如果你覺得像第4小節(jié)講到的那樣一個(gè)一個(gè)submit()任務(wù),然后獲得Future比較繁雜,你可以直接將任務(wù)列表使用invokeAll()方法直接全部給執(zhí)行器,它會返回一個(gè)對應(yīng)的Future列表,對應(yīng)的Future在列表中的位置和提交的任務(wù)一致,invokeAll將所有的任務(wù)提交后,等待全部任務(wù)完成才返回;在延遲后執(zhí)行者運(yùn)行任務(wù)
創(chuàng)建ScheduledThreadPoolExecutor和利用這個(gè)執(zhí)行器安排任務(wù)在指定的時(shí)間后執(zhí)行。它是使用這個(gè)執(zhí)行器對象的schedule()函數(shù)來完成這個(gè)操作的,如:executor.schedule(task, delayTime, TimeUnit.SECONDS);執(zhí)行者定期的執(zhí)行任務(wù)
使用ScheduledThreadPoolExecutor類還可以執(zhí)行定期任務(wù),這一點(diǎn)是調(diào)用執(zhí)行器對象的scheduleAtFixedRate()方法來實(shí)現(xiàn)的。當(dāng)執(zhí)行器對象調(diào)用shutdown()方法后,將不會執(zhí)行周期性任務(wù)。執(zhí)行者取消任務(wù)
通過調(diào)用執(zhí)行器返回的Future的cancel方法,可以取消任務(wù)的執(zhí)行。執(zhí)行者控制一個(gè)結(jié)束任務(wù)
重寫FutureTask類的done()方法來實(shí)現(xiàn)在執(zhí)行者執(zhí)行任務(wù)完成后執(zhí)行額外的一些代碼。執(zhí)行者分離運(yùn)行任務(wù)和處理結(jié)果
使用CompletionService類來實(shí)現(xiàn)一邊執(zhí)行任務(wù)線程,一邊處理執(zhí)行完任務(wù)線程的結(jié)果。原文在這里的解釋不是特別清除,可以參考這里執(zhí)行者控制被拒絕的任務(wù)
實(shí)現(xiàn)RejectedExecutionHandler,來處理線程池shutdown()后提交的任務(wù)。第五章: Fork/Join 框架
介紹
Fork/Join框架被設(shè)計(jì)用來解決可以使用分而治之技術(shù)將任務(wù)分解成更小的問題。具體可以查看該節(jié)內(nèi)容。
創(chuàng)建 Fork/Join 池
創(chuàng)建一個(gè)ForkJoinPool對象來執(zhí)行任務(wù)。創(chuàng)建一個(gè)ForkJoinPool執(zhí)行的ForkJoinTask類。
加入任務(wù)的結(jié)果
在上一節(jié)的基礎(chǔ)上,添加說明如果處理任務(wù)的子線程有返回結(jié)果如何處理。主要就是在Override的compute()函數(shù)中返回結(jié)果,然后可以使用處理任務(wù)的子線程的get方法獲得這個(gè)返回結(jié)果。并且從例子中可以看出,Fork/Join框架可以提交不一樣的子任務(wù),具體表現(xiàn)如下;
所以Fork/Join框架更加強(qiáng)調(diào)的是,父線程等待invokeAll出來的子線程完成后,從invokeAll退出繼續(xù)完成接下來的邏輯程序。父線程從邏輯上來說像是從invokeAll處暫停了(等待子線程完成后退出),但是實(shí)際上父線程的線程資源還可以去執(zhí)行其他沒有被執(zhí)行的任務(wù),但是執(zhí)行的內(nèi)容不是父線程原來的內(nèi)容,這樣可以減少創(chuàng)建的線程的數(shù)量,這使用了work-stealing算法,即分配一個(gè)新的任務(wù)給正在執(zhí)行睡眠任務(wù)的工作線程。
異步運(yùn)行任務(wù)
使用ForkJoinTask的異步方法(比如fork()),來向線程池提交任務(wù)。之前我們使用的是同步方法(比如invokeAll()),這將阻塞父線程。而異步方法將繼續(xù)父線程的內(nèi)容,我們使用異步方法也就意味著不能使用work-stealing算法,除非在父線程中使用調(diào)用join()或get()方法來等待任務(wù)的完成時(shí)。
任務(wù)中拋出異常
處理任務(wù)中拋出的異常,檢查異常不能從compute()中拋出,你必須手動解決(使用try-catch)。未檢查異常將不會輸入任何信息到控制臺。就像沒有發(fā)生一樣,但是拋出非檢查異常的線程和它的所有父輩異常不會正常執(zhí)行。我們自然不會希望,任務(wù)沒有正常執(zhí)行但是又沒有給任何提示,所以最后我們可以使用task.isCompletedAbnormally()來檢查execute()給線程池的任務(wù)到底有沒有拋出非檢查異常。
非檢查異常也可以使用try-catch來捕獲,但是一般我們是不會這樣做的,因?yàn)榉菣z查異常的拋出一般是意味著我們的代碼有錯(cuò)誤,應(yīng)該去改正它。
取消任務(wù)
ForkJoinTask類提供cancel()方法用于這個(gè)目的。當(dāng)你想要取消一個(gè)任務(wù)時(shí),有一些點(diǎn)你必須考慮一下,這些點(diǎn)如下:
- ForkJoinPool類并沒有提供任何方法來取消正在池中運(yùn)行或等待的所有任務(wù)。
- 當(dāng)你取消一個(gè)任務(wù)時(shí),你不能取消一個(gè)已經(jīng)執(zhí)行的任務(wù)。
但是你依舊可以調(diào)用運(yùn)行的任務(wù)的cancel()方法,但是具體執(zhí)不執(zhí)行就要看到底這個(gè)任務(wù)有沒有在運(yùn)行了。可以參考示例代碼中,創(chuàng)建一個(gè)TaskManager類來管理任務(wù)。
第六章: 并發(fā)集合
介紹
這里需要了解到并發(fā)集合分為兩種:- 阻塞集合:這種集合包括添加和刪除數(shù)據(jù)的操作。如果操作不能立即進(jìn)行,是因?yàn)榧弦褲M或者為空,該程序?qū)⒈蛔枞?#xff0c;直到操作可以進(jìn)行。
- 非阻塞集合:這種集合也包括添加和刪除數(shù)據(jù)的操作。如果操作不能立即進(jìn)行,這個(gè)操作將返回null值或拋出異常,但該線程將不會阻塞。
使用非阻塞線程安全列表
非阻塞并發(fā)列表ConcurrentLinkedDeque類。使用阻塞線程安全列表
阻塞列表LinkedBlockingDeque類。用優(yōu)先級對使用阻塞線程安全列表排序
阻塞優(yōu)先隊(duì)列PriorityBlockingQueue類使用線程安全與帶有延遲元素的列表
阻塞隊(duì)列DelayedQueue類,可以控制元素延遲出現(xiàn)使用線程安全的可遍歷映射
ConcurrentSkipListMap提供了多線程并發(fā)存取<Key, Value>數(shù)據(jù)并且希望保證數(shù)據(jù)有序時(shí)的數(shù)據(jù)結(jié)構(gòu),底層使用跳表實(shí)現(xiàn)。關(guān)于ConcurrentSkipListMap更加詳細(xì)的信息可以參考這里生成并行隨機(jī)數(shù)
使用ThreadLocalRandom類來生成隨機(jī)數(shù)使用原子變量
使用原子變量,原子變量不使用任何鎖或者其他同步機(jī)制來保護(hù)它們的值的訪問。他們的全部操作都是基于CAS操作。關(guān)于CAS操作更具體的內(nèi)容網(wǎng)上有很多,務(wù)必理解。特別是要理解底層是如何實(shí)現(xiàn)的。我建議查看這里。特別是其一直深入到底層的分析,非常有意思。使用原子陣列
使用原子數(shù)組,這和原子變量差不多。第七章: 定制并發(fā)類
介紹
定制ThreadPoolExecutor 類
復(fù)寫ThreadPoolExecutor 類的一些方法。這里需要去理解ThreadPoolExecutor類構(gòu)造函數(shù)的一些參數(shù)。
實(shí)現(xiàn)一個(gè)優(yōu)先級制的執(zhí)行者類
實(shí)現(xiàn)一個(gè)按優(yōu)先級執(zhí)行的ThreadPoolExecutor 類,實(shí)際上就是TASK(任務(wù))實(shí)現(xiàn)Comparable接口,而后將PriorityBlockingQueue作為ThreadPoolExecutor的提交任務(wù)時(shí)所使用的隊(duì)列。
實(shí)現(xiàn)ThreadFactory接口來生成自定義線程
使用線程工廠來創(chuàng)造線程
在執(zhí)行者對象中使用我們的 ThreadFactory
以自己的線程工廠為參數(shù)構(gòu)造執(zhí)行器對象,使執(zhí)行器對象使用我們的線程工廠創(chuàng)造線程。
在計(jì)劃好的線程池中定制運(yùn)行任務(wù)
使用ScheduledThreadPoolExecutor 來運(yùn)行計(jì)劃任務(wù)。
實(shí)現(xiàn)ThreadFactory接口來生成自定義線程給Fork/Join框架
實(shí)現(xiàn)ThreadFactory接口創(chuàng)建自己的線程工廠提供給Fork/Join框架使用,而不是使用其默認(rèn)的線程工廠。需要明確的是,不要將任務(wù)(callable或runnable)和線程(thread)混為一談。一個(gè)線程可以在不同的時(shí)期執(zhí)行多個(gè)任務(wù)。線程也可以有自己的內(nèi)存空間,保存自己的數(shù)據(jù)。如果能將任務(wù)和線程這兩個(gè)概念區(qū)分開來,代碼還是比較好理解的。
在Fork/Join框架中定制運(yùn)行任務(wù)
上一節(jié),我們?yōu)镕ork/Join框架創(chuàng)建的線程工廠,這一節(jié)我們將定制我們自己的任務(wù)類,加入到Fork/Join框架的任務(wù)類一般都繼承ForkJoinTask這個(gè)抽象類,一般我們使用:
- RecursiveAction: 如果你的任務(wù)沒有返回結(jié)果
- RecursiveTask: 如果你的任務(wù)返回結(jié)果
但是,這里我們將自己實(shí)現(xiàn)一個(gè)類繼承ForkJoinTask這個(gè)抽象類,從而使任務(wù)運(yùn)行更加更加符合我們的想法。
當(dāng)我們想去實(shí)現(xiàn)和任務(wù)具體的內(nèi)容沒有關(guān)系的功能的時(shí)候,比如統(tǒng)計(jì),控制線程的時(shí)候,我們可以嘗試定制自己的并發(fā)類。
實(shí)現(xiàn)一個(gè)自定義鎖類
實(shí)現(xiàn)Lock接口,從而實(shí)現(xiàn)自定義鎖類。
實(shí)現(xiàn)一個(gè)基于優(yōu)先級傳輸Queue
本節(jié)實(shí)現(xiàn)一個(gè)有優(yōu)先級的傳輸隊(duì)列,用來解決生產(chǎn)者/消費(fèi)者問題。
實(shí)現(xiàn)你自己的原子對象
繼承原子變量,實(shí)現(xiàn)自己的類似原子變量的一些操作,比如并發(fā)安全的加1或者減1。
第八章: 測試并發(fā)應(yīng)用程序
介紹
本節(jié)主要講述如何測試你的并發(fā)應(yīng)用程序是否正確。2-5節(jié)都是說明如何從并發(fā)類中獲取相應(yīng)的信息,也就是通過獲取狀態(tài)變量來對其相關(guān)對象進(jìn)行監(jiān)控。下面每節(jié)的內(nèi)容很直白的體現(xiàn)在小節(jié)題目中了。
監(jiān)控鎖接口
監(jiān)控Phaser類
監(jiān)控執(zhí)行者框架
監(jiān)控Fork/Join池
編寫有效的日志
FindBugs分析并發(fā)代碼
配置Eclipse來調(diào)試并發(fā)代碼
配置NetBeans來調(diào)試并發(fā)代碼
MultithreadedTC測試并發(fā)代碼
總結(jié)
以上是生活随笔為你收集整理的Java 7 并发编程指南的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 理解java并发工具Phaser
- 下一篇: JMM中的原子性、可见性、有序性和vol