《Java大學(xué)教程》—第22章 多線程程序
22.2 進(jìn)程(process):P551
時間切片(time-slicing):處理器只是完成了一個任務(wù)的一部分工作,然后完成下一個任務(wù)的一部分工作,
因為處理呂每次完成工作的時間都非常短,因此看起來這些任務(wù)是同時完成的。
進(jìn)程:一個運(yùn)行的程序通常稱為一個進(jìn)程。
并發(fā)進(jìn)程(concurrent process):兩個或多個可以同時執(zhí)行的進(jìn)程稱為并發(fā)進(jìn)程。
在多個進(jìn)程并發(fā)執(zhí)行時,每個進(jìn)程都有自己的存放程序代碼和數(shù)據(jù)的存儲空間,并且每個進(jìn)程的存儲空間對于其他進(jìn)程都是受保護(hù)的,所有這些工作都由操作系統(tǒng)完成。
22.3 線程(thread):P552
線程:一個程序執(zhí)行的多個獨立任務(wù)稱為線程。通常將線程稱為輕量級進(jìn)程(lightweight process),因為也管理進(jìn)程相比,管理線程需要占用較少的系統(tǒng)資源。
線程沒有完全獨立的存儲空間,線程之間共享代碼區(qū)和存儲區(qū)。
管理線程的方式也是基于時間切片原則,由JVM和OS協(xié)同管理。
22.4 Thread類
繼承Thread類實現(xiàn)線程。控制線程用三個方法:start(), run(), finish()
主要執(zhí)行代碼在run()方法中。
注:run()方法結(jié)束后,這個線程就運(yùn)行結(jié)束了,不可以再次調(diào)用,必須重新創(chuàng)建。原因也可參考圖22-2的狀態(tài)轉(zhuǎn)換圖,線程進(jìn)入TERMINATE狀態(tài)中,無法再次啟動。
22.5 線程的執(zhí)行與調(diào)度
份額(quantum):每個進(jìn)程或者線程都可以獲得處理器上的一小段時間--稱為份額,然后輪轉(zhuǎn)到下一個進(jìn)程或線程。
份額內(nèi)時間沒有用完時,也可以使用sleep()方法,強(qiáng)制放棄CPU的占用權(quán),將CPU還給OS,從而可以繼續(xù)分配給下一個進(jìn)程或線程。
注:sleep()方法傳入的時間間隔(以毫秒為單位),可能會拋出InterruptedException異常,必須寫在try...catch代碼塊中。
22.6 Runnable接口
創(chuàng)建一個實現(xiàn)Runnable接口的類,然后將類的實例作為Runnable接口的參數(shù)傳入Thread對象的構(gòu)造函數(shù)中。
后面的工作主要就是操作Thread的對象,與前面擴(kuò)展Thread類的方法一樣了。
注:此處的Thread對象是獨立創(chuàng)建的,前面是對Thread類的繼承。
22.7 線程同步:P562
異步行為(asynchronous behaviour):
在一般情況下兩個或多個并發(fā)執(zhí)行線程的行為并不是協(xié)同的,而且無法預(yù)知在某個時間段內(nèi)哪個線程將會占用CPU,這種非協(xié)同的行為稱為異步行為。
互斥訪問(mutual exclusion):需要將訪問緩沖區(qū)的程序段看作臨界區(qū)(critical section)--即在任何時刻只能被一個進(jìn)程訪問的區(qū)域,實現(xiàn)臨界區(qū)的方式稱為互斥訪問。
忙等待(busy waiting):被某個線程執(zhí)行的方法循環(huán)中反復(fù)執(zhí)行,直到滿足某個條件才能終止。效率太低,可以使用wait()方法將線程掛起,直到被其他線程的消息喚醒。
在Java中創(chuàng)建一個monitor對象(監(jiān)視器),monitor類的所有方法在任何時刻都只能被一個線程訪問。
可以在方法的頭部使用synchronized修飾符,由于方法被同步(synchronized),所以一旦有某個對象調(diào)用該方法,那么該方法將被置上一個鎖(lock),
其他對象必須在該方法執(zhí)行完成后才能訪問它。
注1:為什么要加鎖?是因為你有臨界區(qū),對這個臨界區(qū)加鎖才是目的。
因此鎖的目的:當(dāng)前類(即this.靜態(tài)變量),當(dāng)前對象(即this.變量),當(dāng)前對象中創(chuàng)建的對象(即this.對象變量),外部傳入到當(dāng)前對象中的對象。
注2:什么可以加鎖?對象,只有對象可以加鎖。
在Java中,每一個對象都擁有一個鎖標(biāo)記(monitor),也稱為監(jiān)視器,多線程同時訪問某個對象時,線程只有獲取了該對象的鎖才能訪問。
JVM會記錄并且監(jiān)管這個鎖,當(dāng)線程獲取鎖后計數(shù)器由0變1,線程若訪問同樣鎖的其他方法,計數(shù)器還會繼續(xù)增加,從而保證線程完全釋放這個鎖后才會允許其他線程訪問。
所以基本數(shù)據(jù)類型的變量是不能作為臨界區(qū)的,也就不能被加鎖。
注3:怎么樣加鎖?使用synchronized對靜態(tài)方法、方法、對象變量加鎖。
static synchronized aStaticMethod(){...} // “當(dāng)前類(即this.靜態(tài)變量)”加鎖
synchronized aMethod(){...} // “當(dāng)前對象(即this.變量)”加鎖
synchronized (aObjectVariable){...} // “當(dāng)前對象中創(chuàng)建的對象(即this.對象變量)”或“外部傳入到當(dāng)前對象中的對象”加鎖。
22.8 線程狀態(tài):P563
狀態(tài)轉(zhuǎn)換圖:圖22-2 一個線程的狀態(tài)轉(zhuǎn)換圖
start()啟動一個線程-->線程進(jìn)入ready(就緒)狀態(tài)。
dispatch()調(diào)度一個線程-->線程進(jìn)入running(運(yùn)行)狀態(tài)。
線程運(yùn)行時間超時、yield()強(qiáng)制一個線程放棄CPU-->線程進(jìn)入ready(就緒)狀態(tài)。
sleep()睡眠一個線程-->線程進(jìn)入sleeping(睡眠)狀態(tài)-->睡眠時間超時-->線程進(jìn)入ready(就緒)狀態(tài)。
wait()-->線程進(jìn)入waiting(等待)狀態(tài)-->獲得其他線程notify()或notifyAll()方法通知-->線程進(jìn)入ready(就緒)狀態(tài)。
由于等待輸入或等待外部設(shè)備可用-->線程處于blocked(阻塞)狀態(tài)-->阻塞時間超時或設(shè)備正常-->線程進(jìn)入ready(就緒)狀態(tài)。
run()方法完成后,線程被終止。
22.10 Timer類:調(diào)度線程
在固定時間間隔內(nèi)生成ActionEvents對象。因此,必須將一個Timer對象關(guān)聯(lián)到一個ActionListener對象。
只要Timer對象生成一個ActionEvent對象,都會執(zhí)行與Timer對象相關(guān)的ActionListener對象的actionPerformed()方法。
自測題:
1. 如何通過時間切片實現(xiàn)并發(fā):P552
時間切片(time-slicing):處理器只是完成了一個任務(wù)的一部分工作,然后完成下一個任務(wù)的一部分工作,
因為處理呂每次完成工作的時間都非常短,因此看起來這些任務(wù)是并發(fā)完成的。
2. 進(jìn)程與線程的區(qū)別:P552
每個進(jìn)程都有完全獨立的存儲空間,用于存放程序的代碼和數(shù)據(jù),并且每個進(jìn)程的存儲空間對于其他進(jìn)程都是受保護(hù)的,所有這些工作都由操作系統(tǒng)完成。
每個線程沒有完全獨立的存儲空間,線程之間共享程序的代碼和數(shù)據(jù),線程的調(diào)度由JVM和操作系統(tǒng)協(xié)同完成。
3. 異步線程執(zhí)行和同步線程執(zhí)行的區(qū)別:P562
異步執(zhí)行線程:無法預(yù)知某個時間段內(nèi)哪個線程將會占用CPU,也無法估計程序執(zhí)行的結(jié)果。
同步執(zhí)行線程:確保線程對于臨界區(qū)是互斥訪問的,當(dāng)某個線程執(zhí)行同步代碼時,其他線程無法再調(diào)用同步代碼,必須等待前面執(zhí)行的進(jìn)程解鎖。
4. 臨界區(qū)和互斥的概念:P562
訪問緩沖區(qū)的程序段看作臨界區(qū)(critical section)--即在任何時刻只能被一個進(jìn)程訪問的區(qū)域。
實現(xiàn)臨界區(qū)的方式稱為互斥訪問(mutual exclusion)。
Jav提供了在多線程程序中實現(xiàn)互斥的機(jī)制,即每個對象都有一個monitor(監(jiān)視器),monitor類的所有方法在任何時刻都只能被一個線程訪問。
只需要在方法的頭部使用synchronized修飾符,由于方法被同步,所以一旦有某個對象調(diào)用該方法,那么這個方法就會被上鎖,其他對象如果同時想訪問就必須等待。
5. 如何在Java程序中避免忙等待:P562
使用wait()方法將線程的執(zhí)行掛起,直到它收到另一個進(jìn)程的消息將它喚醒。就可以避免出現(xiàn)忙等待。
6. Threads6.java RunThreads6.java
編程練習(xí):代碼附件
3. CounterVersionFour.java RunCounterVersion.java CounterThread.java
posted on 2016-10-04 16:36 zYx.Tom 閱讀(290) 評論(0) 編輯 收藏 所屬分類: 1.Java世界