34咆疗、進(jìn)程與線程的區(qū)別
??(1)進(jìn)程概念
??進(jìn)程是表示資源分配的基本單位。 例如母债,用戶(hù)運(yùn)行自己的程序午磁,系統(tǒng)就創(chuàng)建一個(gè)進(jìn)程,并為它分配資源毡们,包括各種表格迅皇、內(nèi)存空間、磁盤(pán)空間衙熔、I/O設(shè)備等登颓。然后,把該進(jìn)程放入進(jìn)程的就緒隊(duì)列红氯。進(jìn)程調(diào)度程序選中它框咙,為它分配CPU以及其它有關(guān)資源,該進(jìn)程才真正運(yùn)行痢甘。所以喇嘱,進(jìn)程是系統(tǒng)中的并發(fā)執(zhí)行的單位。
??在微內(nèi)核系統(tǒng)(Mac塞栅、Windows NT等)中者铜,真正調(diào)度運(yùn)行的基本單位是線程。因此放椰,實(shí)現(xiàn)并發(fā)功能的單位是線程作烟。
??(2)線程概念
??線程是進(jìn)程中執(zhí)行運(yùn)算的最小單位,亦即執(zhí)行處理機(jī)調(diào)度的基本單位砾医。 如果把進(jìn)程理解為在邏輯上操作系統(tǒng)所完成的任務(wù)拿撩,那么線程表示完成該任務(wù)的許多可能的子任務(wù)之一。例如藻烤,假設(shè)用戶(hù)啟動(dòng)了一個(gè)窗口中的數(shù)據(jù)庫(kù)應(yīng)用程序绷雏,操作系統(tǒng)就將對(duì)數(shù)據(jù)庫(kù)的調(diào)用表示為一個(gè)進(jìn)程头滔。假設(shè)用戶(hù)要從數(shù)據(jù)庫(kù)中產(chǎn)生一份工資單報(bào)表怖亭,并傳到一個(gè)文件中,這是一個(gè)子任務(wù)坤检;在產(chǎn)生工資單報(bào)表的過(guò)程中兴猩,用戶(hù)又可以輸人數(shù)據(jù)庫(kù)查詢(xún)請(qǐng)求,這又是一個(gè)子任務(wù)早歇。這樣倾芝,操作系統(tǒng)則把每一個(gè)請(qǐng)求――工資單報(bào)表和新輸人的數(shù)據(jù)查詢(xún)表示為數(shù)據(jù)庫(kù)進(jìn)程中的獨(dú)立的線程讨勤。線程可以在處理器上獨(dú)立調(diào)度執(zhí)行,這樣晨另,在多處理器環(huán)境下就允許幾個(gè)線程各自在單獨(dú)處理器上進(jìn)行潭千。操作系統(tǒng)提供線程就是為了方便而有效地實(shí)現(xiàn)這種并發(fā)性。
??(3)線程與進(jìn)程的比較
??1借尿、調(diào)度:在傳統(tǒng)的操作系統(tǒng)中刨晴,擁有資源的基本單位和獨(dú)立調(diào)度、分派的基本單位都是進(jìn)程路翻。而在引入線程的操作系統(tǒng)中狈癞,則把線程作為調(diào)度和分派的基本單位。而把進(jìn)程作為資源擁有的基本單位茂契,使傳統(tǒng)進(jìn)程的兩個(gè)屬性分開(kāi)蝶桶,線程便能輕裝運(yùn)行,從而可顯著地提高系統(tǒng)的并發(fā)程度掉冶。在同一進(jìn)程中真竖,線程的切換不會(huì)引起進(jìn)程的切換,在由一個(gè)進(jìn)程中的線程切換到另一個(gè)進(jìn)程中的線程時(shí)厌小,將會(huì)引起進(jìn)程的切換疼邀。
??2、并發(fā)性:在引入線程的操作系統(tǒng)中召锈,不僅進(jìn)程之間可以并發(fā)執(zhí)行旁振,而且在一個(gè)進(jìn)程中的多個(gè)線程之間,亦可并發(fā)執(zhí)行涨岁,因而使操作系統(tǒng)具有更好的并發(fā)性拐袜,從而能更有效地使用系統(tǒng)資源和提高系統(tǒng)吞吐量。例如梢薪,在一個(gè)未引入線程的單CPU操作系統(tǒng)中蹬铺,若僅設(shè)置一個(gè)文件服務(wù)進(jìn)程,當(dāng)它由于某種原因而被阻塞時(shí)秉撇,便沒(méi)有其它的文件服務(wù)進(jìn)程來(lái)提供服務(wù)甜攀。在引入了線程的操作系統(tǒng)中,可以在一個(gè)文件服務(wù)進(jìn)程中琐馆,設(shè)置多個(gè)服務(wù)線程规阀,當(dāng)?shù)谝粋€(gè)線程等待時(shí),文件服務(wù)進(jìn)程中的第二個(gè)線程可以繼續(xù)運(yùn)行瘦麸;當(dāng)?shù)诙€(gè)線程阻塞時(shí)谁撼,第三個(gè)線程可以繼續(xù)執(zhí)行,從而顯著地提高了文件服務(wù)的質(zhì)量以及系統(tǒng)吞吐量滋饲。
??3厉碟、擁有資源:不論是傳統(tǒng)的操作系統(tǒng)喊巍,還是設(shè)有線程的操作系統(tǒng),進(jìn)程都是擁有資源的一個(gè)獨(dú)立單位箍鼓,它可以擁有自己的資源崭参。一般地說(shuō),線程自己不擁有系統(tǒng)資源(也有一點(diǎn)必不可少的資源)款咖,但它可以訪問(wèn)其隸屬進(jìn)程的資源阵翎。亦即,一個(gè)進(jìn)程的代碼段之剧、數(shù)據(jù)段以及系統(tǒng)資源郭卫,如已打開(kāi)的文件、I/O設(shè)備等背稼,可供同一進(jìn)程的其它所有線程共享贰军。
??4、系統(tǒng)開(kāi)銷(xiāo):由于在創(chuàng)建或撤消進(jìn)程時(shí)蟹肘,系統(tǒng)都要為之分配或回收資源词疼,如內(nèi)存空間、IO設(shè)備等帘腹。因此贰盗,操作系統(tǒng)所付出的開(kāi)銷(xiāo)將顯著地大于在創(chuàng)建或撤消線程時(shí)的開(kāi)銷(xiāo)。類(lèi)似地阳欲,在進(jìn)行進(jìn)程切換時(shí)舵盈,涉及到整個(gè)當(dāng)前進(jìn)程CPU環(huán)境的保存以及新被調(diào)度運(yùn)行的進(jìn)程的CPU環(huán)境的設(shè)置。而線程切換只須保存和設(shè)置少量寄存器的內(nèi)容球化,并不涉及存儲(chǔ)器管理方面的操作秽晚。可見(jiàn)筒愚,進(jìn)程切換的開(kāi)銷(xiāo)也遠(yuǎn)大于線程切換的開(kāi)銷(xiāo)赴蝇。此外,由于同一進(jìn)程中的多個(gè)線程具有相同的地址空間巢掺,致使它們之間的同步和通信的實(shí)現(xiàn)句伶,也變得比較容易。
??1陆淀、進(jìn)程讓操作系統(tǒng)的并發(fā)成為可能考余,而線程讓進(jìn)程內(nèi)部并發(fā)成為可能。
??2倔约、進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的基本單位秃殉,而線程是操作系統(tǒng)進(jìn)行調(diào)度的基本單位坝初。
??(4)浸剩、并發(fā)與并行
??并發(fā)的關(guān)鍵是有處理多個(gè)任務(wù)的能力钾军,不一定要同時(shí),并行的關(guān)鍵是有同時(shí)處理多個(gè)任務(wù)的能力(可能有多個(gè)CPU)绢要。并發(fā)和并行都可以是很多個(gè)線程吏恭,就看這些線程能不能同時(shí)被(多個(gè))CPU執(zhí)行,如果可以重罪,則是并行樱哼,而并發(fā)是多個(gè)線程被(一個(gè))CPU輪流執(zhí)行。
35剿配、進(jìn)程間的通信方式(IPC)
??1搅幅、管道pipe:管道是一種半雙工的通信方式,數(shù)據(jù)只能單向流動(dòng)呼胚,而且只能在具有親緣關(guān)系的進(jìn)程間使用茄唐。進(jìn)程的親緣關(guān)系通常是指父子進(jìn)程關(guān)系。
??2蝇更、命名管道FIFO:有名管道也是半雙工的通信方式沪编,但是它允許無(wú)親緣關(guān)系進(jìn)程間的通信。
??3年扩、消息隊(duì)列MessageQueue:消息隊(duì)列是由消息的鏈表蚁廓,存放在內(nèi)核中并由消息隊(duì)列標(biāo)識(shí)符標(biāo)識(shí)。消息隊(duì)列克服了信號(hào)傳遞信息少厨幻、管道只能承載無(wú)格式字節(jié)流以及緩沖區(qū)大小受限等缺點(diǎn)相嵌。
??4、共享內(nèi)存SharedMemory:共享內(nèi)存就是映射一段能被其他進(jìn)程所訪問(wèn)的內(nèi)存况脆,這段共享內(nèi)存由一個(gè)進(jìn)程創(chuàng)建平绩,但多個(gè)進(jìn)程都可以訪問(wèn)。共享內(nèi)存是最快的 IPC 方式漠另,它是針對(duì)其他進(jìn)程間通信方式運(yùn)行效率低而專(zhuān)門(mén)設(shè)計(jì)的捏雌。它往往與其他通信機(jī)制,如信號(hào)量笆搓,配合使用性湿,來(lái)實(shí)現(xiàn)進(jìn)程間的同步和通信。
??5满败、信號(hào)量Semaphore:信號(hào)量是一個(gè)計(jì)數(shù)器肤频,可以用來(lái)控制多個(gè)進(jìn)程對(duì)共享資源的訪問(wèn)。它常作為一種鎖機(jī)制算墨,防止某進(jìn)程正在訪問(wèn)共享資源時(shí)宵荒,其他進(jìn)程也訪問(wèn)該資源。因此,主要作為進(jìn)程間以及同一進(jìn)程內(nèi)不同線程之間的同步手段报咳。
??6侠讯、套接字Socket:套接字也是一種進(jìn)程間通信機(jī)制,與其他通信機(jī)制不同的是暑刃,它可用于不同機(jī)器間的進(jìn)程通信厢漩。
??7、信號(hào)signal:信號(hào)是一種比較復(fù)雜的通信方式岩臣,用于通知接收進(jìn)程某個(gè)事件已經(jīng)發(fā)生溜嗜。
35.1、Java線程間通信方式
??1架谎、Object類(lèi)中的wait()/notify()/notifyAll()方法炸宵。
??2、使用Condition接口
??Condition 是被綁定到 Lock 上的谷扣,要?jiǎng)?chuàng)建一個(gè) Lock 的 Condition 對(duì)象必須用 newCondition()方法焙压。在一個(gè) Lock 對(duì)象里面可以創(chuàng)建多個(gè)Condition 對(duì)象,線程可以注冊(cè)在指定的 Condition 對(duì)象中抑钟,從而可以有選擇性地進(jìn)行線程通知涯曲,在線程調(diào)度上更加靈活。
??在 Condition 中在塔,用 await()替換 wait()幻件,用 signal()替換 notify(),用 signalAll()替換 notifyAll()蛔溃,傳統(tǒng)線程的通信方式绰沥,Condition 都可以實(shí)現(xiàn)。調(diào)用 Condition 對(duì)象中的方法時(shí)贺待,需要被包含在 lock()和 unlock()之間徽曲。
??3、管道實(shí)現(xiàn)線程間的通信麸塞。
??實(shí)現(xiàn)方式:一個(gè)線程發(fā)送數(shù)據(jù)到輸出管道流秃臣,另一個(gè)線程從輸入管道流中讀取數(shù)據(jù)。
??基本流程:
??1)哪工、創(chuàng)建管道輸出流 PipedOutputStream pos 和管道輸入流PipedInputStream pis奥此。
??2)、將 pos 和 pis 連接雁比,pos.connect(pis)稚虎。
??3)、將 pos 賦給信息輸入信息的線程偎捎,pis 賦給獲取信息的線程蠢终,就可以實(shí)現(xiàn)線程間的通訊了序攘。
??管道流通信的缺點(diǎn):
??1)、管道流只能在兩個(gè)線程之間傳遞數(shù)據(jù)寻拂。
??2)程奠、管道流只能實(shí)現(xiàn)單向發(fā)送,如果要兩個(gè)線程之間互通訊兜喻,則需要兩個(gè)管道流梦染。
??4赡麦、使用volatile關(guān)鍵字朴皆。volatile關(guān)鍵字能夠保證線程對(duì)共享變量的修改對(duì)其他線程可見(jiàn),但它不保證操作的原子性泛粹。
36遂铡、線程的生命周期(狀態(tài))
??在 Java 當(dāng)中,線程通常都有五種狀態(tài)晶姊,新建(new)扒接、就緒(runnable)、運(yùn)行(running)们衙、阻塞(blocked)和死亡(dead)钾怔。
??第一是新建(new)狀態(tài)。在生成線程對(duì)象蒙挑,并沒(méi)有調(diào)用該對(duì)象的 start 方法宗侦,這是線程處于新建狀態(tài)。
??第二是就緒(runnable)狀態(tài)忆蚀。當(dāng)調(diào)用了線程對(duì)象的 start 方法之后矾利,該線程就進(jìn)入了就緒狀態(tài),但是此時(shí)線程調(diào)度程序還沒(méi)有把該線程設(shè)置為當(dāng)前線程馋袜,此時(shí)處于就緒狀態(tài)男旗。在線程運(yùn)行之后,從等待或者睡眠中回來(lái)之后欣鳖,也會(huì)處于就緒狀態(tài)察皇。
??第三是運(yùn)行(running)狀態(tài)。線程調(diào)度程序?qū)⑻幱诰途w狀態(tài)的線程設(shè)置為當(dāng)前線程泽台,此時(shí)線程就進(jìn)入了運(yùn)行狀態(tài)让网,開(kāi)始運(yùn)行 run 函數(shù)當(dāng)中的代碼。
??第四是阻塞(blocked) 师痕、timed_waiting溃睹、waiting狀態(tài)。線程正在運(yùn)行的時(shí)候胰坟,被暫停因篇,通常是為了等待某個(gè)事件的發(fā)生(比如說(shuō)某項(xiàng)資源就緒)之后再繼續(xù)運(yùn)行泞辐。sleep,wait 等方法都可以導(dǎo)致線程阻塞竞滓。
??第五是死亡(dead)狀態(tài)咐吼。如果一個(gè)線程的 run 方法執(zhí)行結(jié)束或者異常中斷后,該線程就會(huì)死亡商佑。對(duì)于已經(jīng)死亡的線程锯茄,無(wú)法再使用 start 方法令其進(jìn)入就緒。
37茶没、創(chuàng)建線程的幾種方式
??1肌幽、繼承 Thread類(lèi)創(chuàng)建線程類(lèi)
??(1)定義 Thread 類(lèi)的子類(lèi),并重寫(xiě)該類(lèi)的 run 方法抓半,該 run 方法的方法體就代表了線程要完成的任務(wù)喂急。因此把 run()方法稱(chēng)為執(zhí)行體。
??(2)創(chuàng)建 Thread 子類(lèi)的實(shí)例笛求,即創(chuàng)建了線程對(duì)象廊移。
??(3)調(diào)用線程對(duì)象的 start()方法來(lái)啟動(dòng)該線程。
??2探入、通過(guò) Runnable 接口創(chuàng)建線程類(lèi)
??(1)定義 Runnable 接口的實(shí)現(xiàn)類(lèi)狡孔,并重寫(xiě)該接口的 run()方法,該 run()方法的方法體同樣是該線程的線程執(zhí)行體蜂嗽。
??(2)創(chuàng)建 Runnable 實(shí)現(xiàn)類(lèi)的實(shí)例苗膝,并以此實(shí)例作為 Thread 的 target來(lái)創(chuàng)建 Thread 對(duì)象,該 Thread 對(duì)象才是真正的線程對(duì)象徒爹。
??(3)調(diào)用線程對(duì)象的 start()方法來(lái)啟動(dòng)該線程荚醒。
??3、通過(guò) Callable 和 Future 創(chuàng)建線程
??(1)創(chuàng)建 Callable 接口的實(shí)現(xiàn)類(lèi)隆嗅,并實(shí)現(xiàn) call()方法界阁,該 call()方法將作為線程執(zhí)行體,并且有返回值胖喳。
??(2)創(chuàng)建 Callable 實(shí)現(xiàn)類(lèi)的實(shí)例泡躯,使用 FutureTask 類(lèi)來(lái)包裝 Callable對(duì)象,該 FutureTask 對(duì)象封裝了該 Callable 對(duì)象的 call() 方法的返回值丽焊。
??(3)使用 FutureTask 對(duì)象作為 Thread 對(duì)象的 target 創(chuàng)建并啟動(dòng)新線程较剃。
??(4)調(diào)用 FutureTask 對(duì)象的 get()方法來(lái)獲得子線程執(zhí)行結(jié)束后的返回值。
??4技健、通過(guò)線程池創(chuàng)建線程
??利用線程池不用 new 就可以創(chuàng)建線程写穴,線程可復(fù)用,利用 Executors 創(chuàng)建線程池雌贱。
38啊送、Java中用戶(hù)線程和守護(hù)線程的區(qū)別
??當(dāng)我們?cè)?Java 程序中創(chuàng)建一個(gè)線程偿短,它就被稱(chēng)為用戶(hù)線程。將一個(gè)用戶(hù)線程設(shè)置為守護(hù)線程的方法就是在調(diào)用 start() 方法之前馋没,調(diào)用對(duì)象的setDamon(true)方法昔逗。一個(gè)守護(hù)線程是在后臺(tái)執(zhí)行并且不會(huì)阻止 JVM 終止的線程,守護(hù)線程的作用是為其他線程的運(yùn)行提供便利服務(wù)篷朵。當(dāng)沒(méi)有用戶(hù)線程在運(yùn)行的時(shí)候勾怒,JVM關(guān)閉程序并且退出。一個(gè)守護(hù)線程創(chuàng)建的子線程依然是守護(hù)線程声旺。
??守護(hù)線程的一個(gè)典型例子就是垃圾回收器笔链。