用裝飾者模式來(lái)分析IO系統(tǒng)樱蛤,把那一大堆分成資源流和過(guò)濾器流之后赃份,整個(gè)IO系統(tǒng)就簡(jiǎn)單明了的多了。把java的網(wǎng)絡(luò)編程分成底層的Socket編程和高層的URL編程之后浊伙,又有豁然開(kāi)朗的感覺(jué)样漆。那么多線程的:命令模式为障!首先分清楚執(zhí)行器和任務(wù)這兩個(gè)概念。
Thread,Executor产场,ExecutorService等屬于執(zhí)行器鹅髓,而Runnable舞竿,Callable京景,F(xiàn)utureTask屬于任務(wù)。程序運(yùn)行時(shí)需要?jiǎng)?chuàng)建一個(gè)執(zhí)行器骗奖,若干任務(wù)确徙,任務(wù)依附在執(zhí)行器上,由執(zhí)行器啟動(dòng)執(zhí)行并控制任務(wù)执桌。至于ThreadFactory和UnCaughtExceptionHandler是指定執(zhí)行的方式以及拋出異常時(shí)的處理鄙皇,可以分別學(xué)習(xí)。值得一提的是一個(gè)重要的類是Future仰挣,它可以實(shí)現(xiàn)線程有返回值伴逸。返回的結(jié)果就放在Future里面,隨時(shí)可以用get()來(lái)檢查執(zhí)行結(jié)果膘壶。還可以用cancel()來(lái)對(duì)線程執(zhí)行interrupt()错蝴,功能十分強(qiáng)大。
二. 多線程的底層協(xié)調(diào)機(jī)制
編寫(xiě)多線程程序最重要的一個(gè)問(wèn)題是對(duì)共享資源(或者叫共享颓芭,可變的狀態(tài))的訪問(wèn)顷锰,訪問(wèn)共享資源也是線程間相互通信的簡(jiǎn)單方法。而多個(gè)線程一起訪問(wèn)共享資源亡问,就要注意線程安全官紫。編寫(xiě)線程安全的代碼,本質(zhì)上是管理對(duì)狀態(tài)的訪問(wèn)州藕,而且通常是共享的束世,可變的狀態(tài).所謂共享,是指一個(gè)變量可以被多個(gè)線程訪問(wèn)床玻;所謂可變毁涉,是指變量在生命周期內(nèi)可以給便。我們討論的線程安全好像是關(guān)于代碼的笨枯,但真正要做的薪丁,是在不可控制的并發(fā)訪問(wèn)中保護(hù)數(shù)據(jù)。
Java提供底層的協(xié)調(diào)機(jī)制馅精,以控制對(duì)共享資源的訪問(wèn)严嗜。這里的協(xié)調(diào),包括競(jìng)爭(zhēng)洲敢,合作和通信漫玄。
競(jìng)爭(zhēng)是通過(guò)鎖來(lái)實(shí)現(xiàn)的,在訪問(wèn)資源之前要先取得鎖,如果鎖正被其他線程占有睦优,那么本線程就會(huì)阻塞渗常,直到取得鎖。具體的實(shí)現(xiàn)方式有兩種:
synchronized關(guān)鍵字汗盘。Synchronized的語(yǔ)義是想要執(zhí)行被包圍的代碼塊或方法皱碘,必須先取得它聲明的那個(gè)對(duì)象鎖。如果不能取得隐孽,線程到這里就阻塞癌椿,直到取得對(duì)象鎖。至于這樣做是否能夠真正保護(hù)了共享資源的訪問(wèn)菱阵,synchronized關(guān)鍵字是不管的踢俄。需要由程序員自己來(lái)保證:確保共享資源是私有的,所有訪問(wèn)共享資源的地方都加上了synchronized關(guān)鍵字晴及,而且使用的是同一個(gè)對(duì)象鎖都办。Synchronized不僅能保證操作的原子性,還可以保證變量的可見(jiàn)性虑稼。而volatile僅能保證變量的可見(jiàn)性琳钉。(有關(guān)原子性和可見(jiàn)性不知道要放哪里,這里先提一下)
2.使用concurrent.lock动雹。使用concurrent.lock不僅能實(shí)現(xiàn)synchronized的全部功能槽卫,而且能提供更好的性能和更精確的語(yǔ)義。比如胰蝠,synchronized用的是互斥鎖歼培,僅能實(shí)現(xiàn)對(duì)資源的互斥訪問(wèn),為concurrent.lock不僅有互斥鎖茸塞,還有讀寫(xiě)鎖躲庄。把讀鎖和寫(xiě)鎖分開(kāi),寫(xiě)鎖相當(dāng)于互斥鎖钾虐,而讀鎖是共享的噪窘,可以讓多線程同時(shí)讀,以提高性能效扫。
合作:線程之間的合作包括Object.wait/notify
/notifyAll倔监,Thread.interrupt,Condition.await/signal/signalAll菌仁『葡埃可以在資源未準(zhǔn)備好時(shí)調(diào)用Object.wait()使當(dāng)前線程進(jìn)入阻塞,而其他線程在資源準(zhǔn)備好時(shí)調(diào)用notify把所有在Object上阻塞的線程喚醒济丘,進(jìn)入可執(zhí)行狀態(tài)谱秽。Condition.await/signal /signalAll是jdk5提供的更加精細(xì)控制線程的類洽蛀,可以組合不同的Condition來(lái)實(shí)現(xiàn)復(fù)雜的控制。
通信:線程之間的通信通過(guò)PipedReader / PipedWriter組合成管道對(duì)疟赊。不同線程的線程可以在上面讀寫(xiě)郊供,從而實(shí)現(xiàn)通信。程序員可以方便地利用它們來(lái)實(shí)現(xiàn)“生產(chǎn)者-消費(fèi)者”模型近哟。
三.多線程的高層協(xié)調(diào)工具類
用底層的協(xié)調(diào)機(jī)制可以靈活實(shí)現(xiàn)各種各樣的需求驮审,但這樣容易出錯(cuò),對(duì)程序員的要求很高椅挣。JDK1.5推出了很多高層的協(xié)調(diào)工具類头岔,這些類讓我們可以不用再用
synchronized或者Lock來(lái)控制并發(fā),只要簡(jiǎn)單實(shí)用它提供的容器鼠证,同步器等,就可以實(shí)現(xiàn)并發(fā)訪問(wèn)靠抑。主要的類也可以用競(jìng)爭(zhēng)量九,合作及通信來(lái)劃分。主要包括:
1.各種特定用途的容器颂碧,方便線程之間的通信荠列。如BlockingQueue,DelayQueue载城,ConcurrentHashMap肌似,CopyOnWriteArrayList等。
線程安全的基本變量類诉瓦,在包java.util.concurrent.atomic中提供川队。
3.各種用途的同步器synchronizer,如CountDownLatch睬澡,CyclicBarrier固额,Semaphore,Exchange煞聪。
4.計(jì)時(shí)斗躏。TimeUnit類為指定和控制基于超時(shí)的操作提供了多重粒度(包括納秒級(jí))。以代替簡(jiǎn)陋的Thread.sleep昔脯。
附A:并發(fā)編程需要掌握的一些基本概念:
1.摩爾定律:集成電路芯片上所集成的晶體管數(shù)量啄糙,越每隔18個(gè)月便會(huì)翻一番。
2.Amdahl定律:對(duì)計(jì)算機(jī)系統(tǒng)的某個(gè)部件采用優(yōu)化措施后所獲得的計(jì)算機(jī)性能的提高云稚,依賴于這部分的執(zhí)行時(shí)間在整個(gè)運(yùn)行時(shí)間中所占的比率隧饼。
3.競(jìng)爭(zhēng)條件:多個(gè)任務(wù)并發(fā)訪問(wèn)和操作同一數(shù)據(jù)且執(zhí)行結(jié)果與訪問(wèn)的特定順序有關(guān),稱為競(jìng)爭(zhēng)條件碱鳞。(多個(gè)任務(wù)競(jìng)爭(zhēng)響應(yīng)某個(gè)條件桑李,因訪問(wèn)順序不同產(chǎn)生沖突或不一致的情況)。比如“檢查再運(yùn)行”“惰性初始化”。
4.原子操作:任務(wù)在執(zhí)行過(guò)程中不能被打斷的一序列操作
5.復(fù)合操作:任務(wù)在執(zhí)行過(guò)程中可以被打斷的一序列操作
6.不變約束:不變式表達(dá)了對(duì)狀態(tài)的約束贵白,這些狀態(tài)是應(yīng)該符合這個(gè)約束的值的組合率拒。不變式可以代表某種業(yè)務(wù)規(guī)則。
7.先驗(yàn)條件:針對(duì)方法禁荒,規(guī)定了在調(diào)用方法之前必須為真的條件
8.后驗(yàn)條件:針對(duì)方法猬膨,規(guī)定了在調(diào)用方法之后必須為真的條件
- 原子性:(見(jiàn)原子操作)
10.可見(jiàn)性:確保線程對(duì)變量的寫(xiě)入對(duì)其他線程是可見(jiàn)的。即刷新內(nèi)存中的變量呛伴。
附B:參考資料:
Java編程思想 中文版 第四版 by Bruce Eckel
Java并發(fā)編程實(shí)踐 中文版 byBrian Goetz,Doug Lea 等