第一篇:多核多線程題目總結(jié)
2.并行和并發(fā)的概念與區(qū)別:
-如果某個系統(tǒng)支持兩個或多個動作(Action)同時存在,那么這個系統(tǒng)就是一個并發(fā)系統(tǒng)-如果某個系統(tǒng)支持兩個或多個動作同時執(zhí)行,那么這個系統(tǒng)就是一個并行系統(tǒng)
-并發(fā)程序可同時擁有兩個或多個線程。如果程序能夠并行執(zhí)行,則一定是運(yùn)行在多核處理器上,每個線程都將分配到一個獨(dú)立的處理器核上。
-“并行”概念是“并發(fā)”概念的一個子集 3.并行計(jì)算技術(shù)的主要目的:
加速求解問題的速度
例如,給定某應(yīng)用,在單處理器上,串行執(zhí)行需要2 周,這個速度對一般的應(yīng)用而言,是無法忍受的。于是,可以借助并行計(jì)算,使用100 臺處理器,加速50 倍,將執(zhí)行時間縮短為6.72 個小時。
提高求解問題的規(guī)模
例如,在單處理器上,受內(nèi)存資源2GB的限制,只能計(jì)算10 萬個網(wǎng)格,但是,當(dāng)前數(shù)值模擬要求計(jì)算千萬個網(wǎng)格。于是,也可以借助并行計(jì)算,使用100 個處理器,將問題求解規(guī)模線性地?cái)U(kuò)大100 倍。并行計(jì)算的主要目標(biāo):
在并行機(jī)上,解決具有重大挑戰(zhàn)性計(jì)算任務(wù)的科學(xué)、工程及商業(yè)計(jì)算問題,滿足不斷增長的應(yīng)用問題對速度和內(nèi)存資源的需求。
4.并行計(jì)算的主要研究內(nèi)容大致可分為四個方面:
并行機(jī)的高性能特征抽取
充分理解和抽取當(dāng)前并行機(jī)體系結(jié)構(gòu)的高性能特征,提出實(shí)用的并行計(jì)算模型和并行性能評價(jià)方法,指導(dǎo)并行算法的設(shè)計(jì)和并行程序的實(shí)現(xiàn)。
并行算法設(shè)計(jì)與分析
設(shè)計(jì)高效率的并行算法,將應(yīng)用問題分解為可并行計(jì)算的多個子任務(wù),并具體分析這些算法的可行性和效果。
并行實(shí)現(xiàn)技術(shù)
主要包含并行程序設(shè)計(jì)和并行性能優(yōu)化。
并行應(yīng)用
這是并行計(jì)算研究的最終目的。通過驗(yàn)證和確認(rèn)并行程序的正確性和效率,進(jìn)一步將程序發(fā)展為并行應(yīng)用軟件,應(yīng)用于求解實(shí)際問題。同時,結(jié)合實(shí)際應(yīng)用出現(xiàn)的各種問題,不斷地改進(jìn)并行算法和并行程序。5.并行程序執(zhí)行時間
對各個進(jìn)程,墻上時間可進(jìn)一步分解為計(jì)算CPU時間、通信CPU時間、同步開銷時間、同步導(dǎo)致的進(jìn)程空閑時間
計(jì)算CPU時間:進(jìn)程指令執(zhí)行所花費(fèi)的CPU時間,包括程序本身的指令執(zhí)行占用的時間(用戶時間)和系統(tǒng)指令花費(fèi)的時間;
通信CPU時間:進(jìn)程通信花費(fèi)的CPU時間; 同步開銷時間:進(jìn)程同步花費(fèi)的時間;
進(jìn)程空閑時間:進(jìn)程空閑時間是指并行程序執(zhí)行過程中,進(jìn)程所有空閑時間總和(如進(jìn)程阻塞式等待其他進(jìn)程的消息時。此時CPU通常是空閑的,或者處于等待狀態(tài))6.并行程序性能優(yōu)化
最主要的是選擇好的并行算法和通信模式
減少通信量、提高通信粒度
提高通信粒度的有效方法就是減少通信次數(shù),盡可能將可以一次傳遞的數(shù)據(jù)合并起來一起傳遞
全局通信盡量利用高效集合通信算法
對于標(biāo)準(zhǔn)的集合通信,如廣播、規(guī)約、數(shù)據(jù)散發(fā)與收集等,盡量調(diào)用MPI標(biāo)準(zhǔn)庫函數(shù) 挖掘算法的并行度,減少CPU空閑等待
具有數(shù)據(jù)相關(guān)性的計(jì)算過程會導(dǎo)致并行運(yùn)行的部分進(jìn)程空閑等待.在這種情況下,可以考慮改變算法來消除數(shù)據(jù)相關(guān)性
7.順序程序的特性
順序性:處理機(jī)嚴(yán)格按照指令次序依次執(zhí)行,即僅當(dāng)一條指令執(zhí)行完后才開始執(zhí)行下一條指令;
封閉性:程序在執(zhí)行過程中獨(dú)占系統(tǒng)中的全部資源,該程序的運(yùn)行環(huán)境只與其自身動作有關(guān),不受其它程序及外界因素影響;
可再現(xiàn)性:程序的執(zhí)行結(jié)果與執(zhí)行速度無關(guān),而只與初始條件有關(guān),給定相同的初始條件,程序的任意多次執(zhí)行一定得到相同的執(zhí)行結(jié)果.8.并發(fā)程序特性
交叉性:程序并發(fā)執(zhí)行對應(yīng)某一種交叉,不同的交叉可能導(dǎo)致不同的計(jì)算結(jié)果,操作系統(tǒng)應(yīng)當(dāng)保證只產(chǎn)生導(dǎo)致正確結(jié)果的交叉,去除那些可能導(dǎo)致不正確結(jié)果的交叉;
非封閉性:一個進(jìn)程的運(yùn)行環(huán)境可能被其它進(jìn)程所改變,從而相互影響;
不可再現(xiàn)性:由于交叉的隨機(jī)性,并發(fā)程序的多次執(zhí)行可能對應(yīng)不同的交叉,因而不能期望重新運(yùn)行的程序能夠再現(xiàn)上次運(yùn)行的結(jié)果。
10.Win32線程同步的實(shí)現(xiàn)
11.數(shù)據(jù)作用域?qū)?shù)據(jù)值的影響
12.分析程序的結(jié)果
13.并行區(qū)域編程與parallel for語句的區(qū)別
-并行區(qū)域采用了復(fù)制執(zhí)行方式,將代碼在所有的線程內(nèi)各執(zhí)行一次;
-循環(huán)并行化則是采用工作分配執(zhí)行方式,將循環(huán)需做的所有工作量,按一定的方式分配給各個執(zhí)行線程,全部線程執(zhí)行工作的總合等于原先串行執(zhí)行所完成的工作量。14.OpenMP提供三種不同的互斥鎖機(jī)制: 臨界區(qū)(critical)原子操作(atomic)由庫函數(shù)來提供同步操作 int counter=0;#pragma omp parallel {
for(int i=0;i<10000;i++)
#pragma omp atomic //atomic operation
}
printf(“counter = %dn”,counter);
counter=20000 25.影響性能的主要因素
并行化代碼在應(yīng)用程序中的比率-OpenMP 本身的開銷
-OpenMP 獲得應(yīng)用程序多線程并行化的能力,需要一定的程序庫支持。在這些庫程序?qū)Τ绦虿⑿屑铀俚耐瑫r也需要運(yùn)行庫本身-負(fù)載均衡
-局部性
在程序運(yùn)行過程中,高速緩存將緩存最近剛剛訪問過的數(shù)據(jù)及其相鄰的數(shù)據(jù)。因此,在編寫程序的時候,需要考慮到高速緩存的作用,有意地運(yùn)用這種局部性帶來的高速緩存的效率提高。-線程同步帶來的開銷
多個線程在進(jìn)行同步的時候必然帶來一定的同步開銷,在使用多線程進(jìn)行開發(fā)時需要考慮同步的必要性,消除不必要的同步,或者調(diào)整同步的順序,就有可能帶來性能上的提升。26.什么是MPI 消息傳遞接口(Message Passing Interface,簡稱MPI)是一種編程接口標(biāo)準(zhǔn),而不是一種具體的編程語言?;竞x:MPI標(biāo)準(zhǔn)定義了一組具有可移植性的編程接口。
特征:
1、典型的實(shí)現(xiàn)包括開源的MPICH、LAM MPI以及不開源的INTEL MPI。
2、程序員設(shè)計(jì)好應(yīng)用程序并行算法,調(diào)用這些接口,鏈接相應(yīng)平臺上的MPI庫,即可實(shí)現(xiàn)基于消息傳遞的并行計(jì)算
27.MPICH的安裝和配置 counter++;
28.MPI程序的四個基本函數(shù)-MPI_Init和MPI_Finalize MPI_Init初始化MPI執(zhí)行環(huán)境,建立多個MPI進(jìn)程之間的聯(lián)系,為后續(xù)通信做準(zhǔn)備。而MPI_Finalize則是結(jié)束MPI執(zhí)行環(huán)境。這兩個函數(shù)用來定義MPI程序的并行區(qū)-MPI_Comm_rank
用來標(biāo)識各個MPI進(jìn)程,兩個函數(shù)參數(shù):
MPI_Comm類型的通信域,表示參與計(jì)算的MPI進(jìn)程組 &rank,返回調(diào)用進(jìn)程在comm中的標(biāo)識號-MPI_Comm_size 用來標(biāo)識相應(yīng)進(jìn)程組中有多少個進(jìn)程,有兩個參數(shù): MPI_Comm類型的通信域,標(biāo)尺參與計(jì)算的MPI進(jìn)程組 整型指針,返回相應(yīng)進(jìn)程組中的進(jìn)程數(shù)
29.MPI的點(diǎn)對點(diǎn)通信
兩個最重要的MPI函數(shù)MPI_Send和MPI_Recv。
-int MPI_SEND(buf, count, datatype, dest, tag, comm)這個函數(shù)的含義是向通信域comm中的dest進(jìn)程發(fā)送數(shù)據(jù)。消息數(shù)據(jù)存放在buf中,類型是datatype,個數(shù)是count個。這個消息的標(biāo)志是tag,用以和本進(jìn)程向同一目的進(jìn)程發(fā)送的其他消息區(qū)別開來。
-int MPI_RECV(buf,count,datatype,source,tag,comm,status)MPI_Recv絕大多數(shù)的參數(shù)和MPI_Send相對應(yīng),有相同的意義。唯一的區(qū)別就是MPI_Recv里面多了一個參數(shù)status。status主要顯示接收函數(shù)的各種錯誤狀態(tài)。30.消息管理7要素 發(fā)送或者接收緩沖區(qū)buf; 數(shù)據(jù)數(shù)量count; 數(shù)據(jù)類型datatype;
目標(biāo)進(jìn)程或者源進(jìn)程destination/source; 消息標(biāo)簽tag; 通信域comm;.消息狀態(tài)status,只在接收的函數(shù)中出現(xiàn)。31.MPI群集通信
群集通信是包含了一對多、多對一和多對多的進(jìn)程通信模式。其最大特點(diǎn)是多個進(jìn)程參與通信。常用的MPI群集通信函數(shù): 同步
廣播 聚集 播撒
第二篇:Java多線程編程總結(jié)
Java多線程編程總結(jié)
2007-05-17 11:21:59 標(biāo)簽:java 多線程
原創(chuàng)作品,允許轉(zhuǎn)載,轉(zhuǎn)載時請務(wù)必以超鏈接形式標(biāo)明文章 原始出處、作者信息和本聲明。否則將追究法律責(zé)任。http://lavasoft.blog.51cto.com/62575/27069
Java多線程編程總結(jié)
下面是Java線程系列博文的一個編目:
Java線程:概念與原理 Java線程:創(chuàng)建與啟動
Java線程:線程棧模型與線程的變量 Java線程:線程狀態(tài)的轉(zhuǎn)換 Java線程:線程的同步與鎖 Java線程:線程的交互 Java線程:線程的調(diào)度-休眠 Java線程:線程的調(diào)度-優(yōu)先級 Java線程:線程的調(diào)度-讓步 Java線程:線程的調(diào)度-合并 Java線程:線程的調(diào)度-守護(hù)線程 Java線程:線程的同步-同步方法 Java線程:線程的同步-同步塊
Java線程:并發(fā)協(xié)作-生產(chǎn)者消費(fèi)者模型 Java線程:并發(fā)協(xié)作-死鎖 Java線程:volatile關(guān)鍵字 Java線程:新特征-線程池
Java線程:新特征-有返回值的線程 Java線程:新特征-鎖(上)Java線程:新特征-鎖(下)Java線程:新特征-信號量 Java線程:新特征-阻塞隊(duì)列 Java線程:新特征-阻塞棧 Java線程:新特征-條件變量 Java線程:新特征-原子量 Java線程:新特征-障礙器 Java線程:大總結(jié)
----
下面的內(nèi)容是很早之前寫的,內(nèi)容不夠充實(shí),而且是基于Java1.4的內(nèi)容,Java5之后,線程并發(fā)部分?jǐn)U展了相當(dāng)多的內(nèi)容,因此建議大家看上面的系列文章的內(nèi)容,與時俱進(jìn),跟上Java發(fā)展的步伐。----
一、認(rèn)識多任務(wù)、多進(jìn)程、單線程、多線程 要認(rèn)識多線程就要從操作系統(tǒng)的原理說起。
以前古老的DOS操作系統(tǒng)(V 6.22)是單任務(wù)的,還沒有線程的概念,系統(tǒng)在每次只能做一件事情。比如你在copy東西的時候不能rename文件名。為了提高系統(tǒng)的利用效率,采用批處理來批量執(zhí)行任務(wù)。
現(xiàn)在的操作系統(tǒng)都是多任務(wù)操作系統(tǒng),每個運(yùn)行的任務(wù)就是操作系統(tǒng)所做的一件事情,比如你在聽歌的同時還在用MSN和好友聊天。聽歌和聊天就是兩個任務(wù),這個兩個任務(wù)是“同時”進(jìn)行的。一個任務(wù)一般對應(yīng)一個進(jìn)程,也可能包含好幾個進(jìn)程。比如運(yùn)行的MSN就對應(yīng)一個MSN的進(jìn)程,如果你用的是windows系統(tǒng),你就可以在任務(wù)管理器中看到操作系統(tǒng)正在運(yùn)行的進(jìn)程信息。
一般來說,當(dāng)運(yùn)行一個應(yīng)用程序的時候,就啟動了一個進(jìn)程,當(dāng)然有些會啟動多個進(jìn)程。啟動進(jìn)程的時候,操作系統(tǒng)會為進(jìn)程分配資源,其中最主要的資源是內(nèi)存空間,因?yàn)槌绦蚴窃趦?nèi)存中運(yùn)行的。在進(jìn)程中,有些程序流程塊是可以亂序執(zhí)行的,并且這個代碼塊可以同時被多次執(zhí)行。實(shí)際上,這樣的代碼塊就是線程體。線程是進(jìn)程中亂序執(zhí)行的代碼流程。當(dāng)多個線程同時運(yùn)行的時候,這樣的執(zhí)行模式成為并發(fā)執(zhí)行。
多線程的目的是為了最大限度的利用CPU資源。
Java編寫程序都運(yùn)行在在Java虛擬機(jī)(JVM)中,在JVM的內(nèi)部,程序的多任務(wù)是通過線程來實(shí)現(xiàn)的。每用java命令啟動一個java應(yīng)用程序,就會啟動一個JVM進(jìn)程。在同一個JVM進(jìn)程中,有且只有一個進(jìn)程,就是它自己。在這個JVM環(huán)境中,所有程序代碼的運(yùn)行都是以線程來運(yùn)行。
一般常見的Java應(yīng)用程序都是單線程的。比如,用java命令運(yùn)行一個最簡單的HelloWorld的Java應(yīng)用程序時,就啟動了一個JVM進(jìn)程,JVM找到程序程序的入口點(diǎn)main(),然后運(yùn)行main()方法,這樣就產(chǎn)生了一個線程,這個線程稱之為主線程。當(dāng)main方法結(jié)束后,主線程運(yùn)行完成。JVM進(jìn)程也隨即退出。
對于一個進(jìn)程中的多個線程來說,多個線程共享進(jìn)程的內(nèi)存塊,當(dāng)有新的線程產(chǎn)生的時候,操作系統(tǒng)不分配新的內(nèi)存,而是讓新線程共享原有的進(jìn)程塊的內(nèi)存。因此,線程間的通信很容易,速度也很快。不同的進(jìn)程因?yàn)樘幱诓煌膬?nèi)存塊,因此進(jìn)程之間的通信相對困難。
實(shí)際上,操作的系統(tǒng)的多進(jìn)程實(shí)現(xiàn)了多任務(wù)并發(fā)執(zhí)行,程序的多線程實(shí)現(xiàn)了進(jìn)程的并發(fā)執(zhí)行。多任務(wù)、多進(jìn)程、多線程的前提都是要求操作系統(tǒng)提供多任務(wù)、多進(jìn)程、多線程的支持。
在Java程序中,JVM負(fù)責(zé)線程的調(diào)度。線程調(diào)度是值按照特定的機(jī)制為多個線程分配CPU的使用權(quán)。調(diào)度的模式有兩種:分時調(diào)度和搶占式調(diào)度。分時調(diào)度是所有線程輪流獲得CPU使用權(quán),并平均分配每個線程占用CPU的時間;搶占式調(diào)度是根據(jù)線程的優(yōu)先級別來獲取CPU的使用權(quán)。JVM的線程調(diào)度模式采用了搶占式模式。
所謂的“并發(fā)執(zhí)行”、“同時”其實(shí)都不是真正意義上的“同時”。眾所周知,CPU都有個時鐘頻率,表示每秒中能執(zhí)行cpu指令的次數(shù)。在每個時鐘周期內(nèi),CPU實(shí)際上只能去執(zhí)行一條(也有可能多條)指令。操作系統(tǒng)將進(jìn)程線程進(jìn)行管理,輪流(沒有固定的順序)分配每個進(jìn)程很短的一段是時間(不一定是均分),然后在每個線程內(nèi)部,程序代碼自己處理該進(jìn)程內(nèi)部線程的時間分配,多個線程之間相互的切換去執(zhí)行,這個切換時間也是非常短的。因此多任務(wù)、多進(jìn)程、多線程都是操作系統(tǒng)給人的一種宏觀感受,從微觀角度看,程序的運(yùn)行是異步執(zhí)行的。
用一句話做總結(jié):雖然操作系統(tǒng)是多線程的,但CPU每一時刻只能做一件事,和人的大腦是一樣的,呵呵。
二、Java與多線程
Java語言的多線程需要操作系統(tǒng)的支持。
Java 虛擬機(jī)允許應(yīng)用程序并發(fā)地運(yùn)行多個執(zhí)行線程。Java語言提供了多線程編程的擴(kuò)展點(diǎn),并給出了功能強(qiáng)大的線程控制API。
在Java中,多線程的實(shí)現(xiàn)有兩種方式: 擴(kuò)展java.lang.Thread類 實(shí)現(xiàn)java.lang.Runnable接口
每個線程都有一個優(yōu)先級,高優(yōu)先級線程的執(zhí)行優(yōu)先于低優(yōu)先級線程。每個線程都可以或不可以標(biāo)記為一個守護(hù)程序。當(dāng)某個線程中運(yùn)行的代碼創(chuàng)建一個新 Thread 對象時,該新線程的初始優(yōu)先級被設(shè)定為創(chuàng)建線程的優(yōu)先級,并且當(dāng)且僅當(dāng)創(chuàng)建線程是守護(hù)線程時,新線程才是守護(hù)程序。
當(dāng) Java 虛擬機(jī)啟動時,通常都會有單個非守護(hù)線程(它通常會調(diào)用某個指定類的 main 方法)。Java 虛擬機(jī)會繼續(xù)執(zhí)行線程,直到下列任一情況出現(xiàn)時為止:
調(diào)用了 Runtime 類的 exit 方法,并且安全管理器允許退出操作發(fā)生。
非守護(hù)線程的所有線程都已停止運(yùn)行,無論是通過從對 run 方法的調(diào)用中返回,還是通過拋出一個傳播到 run 方法之外的異常。
三、擴(kuò)展java.lang.Thread類
/** * File Name: TestMitiThread.java * Created by: IntelliJ IDEA.* Copyright: Copyright(c)2003-2006 * Company: Lavasoft([url]http://lavasoft.blog.51cto.com/[/url])* Author: leizhimin * Modifier: leizhimin * Date Time: 2007-5-17 10:03:12 * Readme: 通過擴(kuò)展Thread類實(shí)現(xiàn)多線程 */ public class TestMitiThread { public static void main(String[] rags){ System.out.println(Thread.currentThread().getName()+ “ 線程運(yùn)行開始!”);new MitiSay(“A”).start();new MitiSay(“B”).start();System.out.println(Thread.currentThread().getName()+ “ 線程運(yùn)行結(jié)束!”);} }
class MitiSay extends Thread { public MitiSay(String threadName){ super(threadName);}
public void run(){ System.out.println(getName()+ “ 線程運(yùn)行開始!”);for(int i = 0;i < 10;i++){ System.out.println(i + “ ” + getName());try { sleep((int)Math.random()* 10);} catch(InterruptedException e){ e.printStackTrace();} } System.out.println(getName()+ “ 線程運(yùn)行結(jié)束!”);} }
運(yùn)行結(jié)果:
main 線程運(yùn)行開始!main 線程運(yùn)行結(jié)束!A 線程運(yùn)行開始!0 A 1 A B 線程運(yùn)行開始!2 A 0 B 3 A 4 A 1 B 5 A 6 A 7 A 8 A 9 A A 線程運(yùn)行結(jié)束!2 B 3 B 4 B 5 B 6 B 7 B 8 B 9 B B 線程運(yùn)行結(jié)束!說明:
程序啟動運(yùn)行main時候,java虛擬機(jī)啟動一個進(jìn)程,主線程main在main()調(diào)用時候被創(chuàng)建。隨著調(diào)用MitiSay的兩個對象的start方法,另外兩個線程也啟動了,這樣,整個應(yīng)用就在多線程下運(yùn)行。
在一個方法中調(diào)用Thread.currentThread().getName()方法,可以獲取當(dāng)前線程的名字。在mian方法中調(diào)用該方法,獲取的是主線程的名字。
注意:start()方法的調(diào)用后并不是立即執(zhí)行多線程代碼,而是使得該線程變?yōu)榭蛇\(yùn)行態(tài)(Runnable),什么時候運(yùn)行是由操作系統(tǒng)決定的。
從程序運(yùn)行的結(jié)果可以發(fā)現(xiàn),多線程程序是亂序執(zhí)行。因此,只有亂序執(zhí)行的代碼才有必要設(shè)計(jì)為多線程。
Thread.sleep()方法調(diào)用目的是不讓當(dāng)前線程獨(dú)自霸占該進(jìn)程所獲取的CPU資源,以留出一定時間給其他線程執(zhí)行的機(jī)會。
實(shí)際上所有的多線程代碼執(zhí)行順序都是不確定的,每次執(zhí)行的結(jié)果都是隨機(jī)的。
四、實(shí)現(xiàn)java.lang.Runnable接口
/** * 通過實(shí)現(xiàn) Runnable 接口實(shí)現(xiàn)多線程 */ public class TestMitiThread1 implements Runnable {
public static void main(String[] args){ System.out.println(Thread.currentThread().getName()+ “ 線程運(yùn)行開始!”);TestMitiThread1 test = new TestMitiThread1();Thread thread1 = new Thread(test);Thread thread2 = new Thread(test);thread1.start();thread2.start();System.out.println(Thread.currentThread().getName()+ “ 線程運(yùn)行結(jié)束!”);}
public void run(){ System.out.println(Thread.currentThread().getName()+ “ 線程運(yùn)行開始!”);for(int i = 0;i < 10;i++){ System.out.println(i + “ ” + Thread.currentThread().getName());try { Thread.sleep((int)Math.random()* 10);} catch(InterruptedException e){ e.printStackTrace();} } System.out.println(Thread.currentThread().getName()+ “ 線程運(yùn)行結(jié)束!”);} }
運(yùn)行結(jié)果:
main 線程運(yùn)行開始!Thread-0 線程運(yùn)行開始!main 線程運(yùn)行結(jié)束!0 Thread-0 Thread-1 線程運(yùn)行開始!0 Thread-1 1 Thread-1 1 Thread-0 2 Thread-0 2 Thread-1 3 Thread-0 3 Thread-1 4 Thread-0 4 Thread-1 5 Thread-0 6 Thread-0 5 Thread-1 7 Thread-0 8 Thread-0 6 Thread-1 9 Thread-0 7 Thread-1 Thread-0 線程運(yùn)行結(jié)束!8 Thread-1 9 Thread-1 Thread-1 線程運(yùn)行結(jié)束!說明:
TestMitiThread1類通過實(shí)現(xiàn)Runnable接口,使得該類有了多線程類的特征。run()方法是多線程程序的一個約定。所有的多線程代碼都在run方法里面。Thread類實(shí)際上也是實(shí)現(xiàn)了Runnable接口的類。
在啟動的多線程的時候,需要先通過Thread類的構(gòu)造方法Thread(Runnable target)構(gòu)造出對象,然后調(diào)用Thread對象的start()方法來運(yùn)行多線程代碼。
實(shí)際上所有的多線程代碼都是通過運(yùn)行Thread的start()方法來運(yùn)行的。因此,不管是擴(kuò)展Thread類還是實(shí)現(xiàn)Runnable接口來實(shí)現(xiàn)多線程,最終還是通過Thread的對象的API來控制線程的,熟悉Thread類的API是進(jìn)行多線程編程的基礎(chǔ)。
五、讀解Thread類API
static int MAX_PRIORITY 線程可以具有的最高優(yōu)先級。static int MIN_PRIORITY 線程可以具有的最低優(yōu)先級。static int NORM_PRIORITY 分配給線程的默認(rèn)優(yōu)先級。
構(gòu)造方法摘要
Thread(Runnable target)分配新的 Thread 對象。Thread(String name)分配新的 Thread 對象。
方法摘要
static Thread currentThread()返回對當(dāng)前正在執(zhí)行的線程對象的引用。ClassLoader getContextClassLoader()返回該線程的上下文 ClassLoader。long getId()返回該線程的標(biāo)識符。String getName()返回該線程的名稱。int getPriority()返回線程的優(yōu)先級。Thread.State getState()返回該線程的狀態(tài)。ThreadGroup getThreadGroup()返回該線程所屬的線程組。static boolean holdsLock(Object obj)當(dāng)且僅當(dāng)當(dāng)前線程在指定的對象上保持監(jiān)視器鎖時,才返回 true。void interrupt()中斷線程。
static boolean interrupted()測試當(dāng)前線程是否已經(jīng)中斷。boolean isAlive()測試線程是否處于活動狀態(tài)。boolean isDaemon()測試該線程是否為守護(hù)線程。boolean isInterrupted()測試線程是否已經(jīng)中斷。void join()等待該線程終止。void join(long millis)等待該線程終止的時間最長為 millis 毫秒。void join(long millis, int nanos)等待該線程終止的時間最長為 millis 毫秒 + nanos 納秒。void resume()已過時。該方法只與 suspend()一起使用,但 suspend()已經(jīng)遭到反對,因?yàn)樗哂兴梨i傾向。有關(guān)更多信息,請參閱為何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對?。void run()如果該線程是使用獨(dú)立的 Runnable 運(yùn)行對象構(gòu)造的,則調(diào)用該 Runnable 對象的 run 方法;否則,該方法不執(zhí)行任何操作并返回。void setContextClassLoader(ClassLoader cl)設(shè)置該線程的上下文 ClassLoader。void setDaemon(boolean on)將該線程標(biāo)記為守護(hù)線程或用戶線程。
static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)設(shè)置當(dāng)線程由于未捕獲到異常而突然終止,并且沒有為該線程定義其他處理程序時所調(diào)用的默認(rèn)處理程序。void setName(String name)改變線程名稱,使之與參數(shù) name 相同。void setPriority(int newPriority)更改線程的優(yōu)先級。
void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)設(shè)置該線程由于未捕獲到異常而突然終止時調(diào)用的處理程序。static void sleep(long millis)在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行)。static void sleep(long millis, int nanos)在指定的毫秒數(shù)加指定的納秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行)。void start()使該線程開始執(zhí)行;Java 虛擬機(jī)調(diào)用該線程的 run 方法。void stop()已過時。該方法具有固有的不安全性。用 Thread.stop 來終止線程將釋放它已經(jīng)鎖定的所有監(jiān)視器(作為沿堆棧向上傳播的未檢查 ThreadDeath 異常的一個自然后果)。如果以前受這些監(jiān)視器保護(hù)的任何對象都處于一種不一致的狀態(tài),則損壞的對象將對其他線程可見,這有可能導(dǎo)致任意的行為。stop 的許多使用都應(yīng)由只修改某些變量以指示目標(biāo)線程應(yīng)該停止運(yùn)行的代碼來取代。目標(biāo)線程應(yīng)定期檢查該變量,并且如果該變量指示它要停止運(yùn)行,則從其運(yùn)行方法依次返回。如果目標(biāo)線程等待很長時間(例如基于一個條件變量),則應(yīng)使用 interrupt 方法來中斷該等待。有關(guān)更多信息,請參閱《為何不贊成使用 Thread.stop、Thread.suspend 和 Thread.resume?》。void stop(Throwable obj)已過時。該方法具有固有的不安全性。請參閱 stop()以獲得詳細(xì)信息。該方法的附加危險(xiǎn)是它可用于生成目標(biāo)線程未準(zhǔn)備處理的異常(包括若沒有該方法該線程不太可能拋出的已檢查的異常)。有關(guān)更多信息,請參閱為何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對?。void suspend()已過時。該方法已經(jīng)遭到反對,因?yàn)樗哂泄逃械乃梨i傾向。如果目標(biāo)線程掛起時在保護(hù)關(guān)鍵系統(tǒng)資源的監(jiān)視器上保持有鎖,則在目標(biāo)線程重新開始以前任何線程都不能訪問該資源。如果重新開始目標(biāo)線程的線程想在調(diào)用 resume 之前鎖定該監(jiān)視器,則會發(fā)生死鎖。這類死鎖通常會證明自己是“凍結(jié)”的進(jìn)程。有關(guān)更多信息,請參閱為何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對?。String toString()返回該線程的字符串表示形式,包括線程名稱、優(yōu)先級和線程組。static void yield()暫停當(dāng)前正在執(zhí)行的線程對象,并執(zhí)行其他線程。
六、線程的狀態(tài)轉(zhuǎn)換圖
線程在一定條件下,狀態(tài)會發(fā)生變化。線程變化的狀態(tài)轉(zhuǎn)換圖如下:
1、新建狀態(tài)(New):新創(chuàng)建了一個線程對象。
2、就緒狀態(tài)(Runnable):線程對象創(chuàng)建后,其他線程調(diào)用了該對象的start()方法。該狀態(tài)的線程位于可運(yùn)行線程池中,變得可運(yùn)行,等待獲取CPU的使用權(quán)。
3、運(yùn)行狀態(tài)(Running):就緒狀態(tài)的線程獲取了CPU,執(zhí)行程序代碼。
4、阻塞狀態(tài)(Blocked):阻塞狀態(tài)是線程因?yàn)槟撤N原因放棄CPU使用權(quán),暫時停止運(yùn)行。直到線程進(jìn)入就緒狀態(tài),才有機(jī)會轉(zhuǎn)到運(yùn)行狀態(tài)。阻塞的情況分三種:
(一)、等待阻塞:運(yùn)行的線程執(zhí)行wait()方法,JVM會把該線程放入等待池中。
(二)、同步阻塞:運(yùn)行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM會把該線程放入鎖池中。
(三)、其他阻塞:運(yùn)行的線程執(zhí)行sleep()或join()方法,或者發(fā)出了I/O請求時,JVM會把該線程置為阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉(zhuǎn)入就緒狀態(tài)。
5、死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期。
七、線程的調(diào)度
1、調(diào)整線程優(yōu)先級:Java線程有優(yōu)先級,優(yōu)先級高的線程會獲得較多的運(yùn)行機(jī)會。
Java線程的優(yōu)先級用整數(shù)表示,取值范圍是1~10,Thread類有以下三個靜態(tài)常量: static int MAX_PRIORITY 線程可以具有的最高優(yōu)先級,取值為10。static int MIN_PRIORITY 線程可以具有的最低優(yōu)先級,取值為1。static int NORM_PRIORITY 分配給線程的默認(rèn)優(yōu)先級,取值為5。
Thread類的setPriority()和getPriority()方法分別用來設(shè)置和獲取線程的優(yōu)先級。
每個線程都有默認(rèn)的優(yōu)先級。主線程的默認(rèn)優(yōu)先級為Thread.NORM_PRIORITY。
線程的優(yōu)先級有繼承關(guān)系,比如A線程中創(chuàng)建了B線程,那么B將和A具有相同的優(yōu)先級。JVM提供了10個線程優(yōu)先級,但與常見的操作系統(tǒng)都不能很好的映射。如果希望程序能移植到各個操作系統(tǒng)中,應(yīng)該僅僅使用Thread類有以下三個靜態(tài)常量作為優(yōu)先級,這樣能保證同樣的優(yōu)先級采用了同樣的調(diào)度方式。
2、線程睡眠:Thread.sleep(long millis)方法,使線程轉(zhuǎn)到阻塞狀態(tài)。millis參數(shù)設(shè)定睡眠的時間,以毫秒為單位。當(dāng)睡眠結(jié)束后,就轉(zhuǎn)為就緒(Runnable)狀態(tài)。sleep()平臺移植性好。
3、線程等待:Object類中的wait()方法,導(dǎo)致當(dāng)前的線程等待,直到其他線程調(diào)用此對象的 notify()方法或 notifyAll()喚醒方法。這個兩個喚醒方法也是Object類中的方法,行為等價(jià)于調(diào)用 wait(0)一樣。
4、線程讓步:Thread.yield()方法,暫停當(dāng)前正在執(zhí)行的線程對象,把執(zhí)行機(jī)會讓給相同或者更高優(yōu)先級的線程。
5、線程加入:join()方法,等待其他線程終止。在當(dāng)前線程中調(diào)用另一個線程的join()方法,則當(dāng)前線程轉(zhuǎn)入阻塞狀態(tài),直到另一個進(jìn)程運(yùn)行結(jié)束,當(dāng)前線程再由阻塞轉(zhuǎn)為就緒狀態(tài)。
6、線程喚醒:Object類中的notify()方法,喚醒在此對象監(jiān)視器上等待的單個線程。如果所有線程都在此對象上等待,則會選擇喚醒其中一個線程。選擇是任意性的,并在對實(shí)現(xiàn)做出決定時發(fā)生。線程通過調(diào)用其中一個 wait 方法,在對象的監(jiān)視器上等待。直到當(dāng)前的線程放棄此對象上的鎖定,才能繼續(xù)執(zhí)行被喚醒的線程。被喚醒的線程將以常規(guī)方式與在該對象上主動同步的其他所有線程進(jìn)行競爭;例如,喚醒的線程在作為鎖定此對象的下一個線程方面沒有可靠的特權(quán)或劣勢。類似的方法還有一個notifyAll(),喚醒在此對象監(jiān)視器上等待的所有線程。注意:Thread中suspend()和resume()兩個方法在JDK1.5中已經(jīng)廢除,不再介紹。因?yàn)橛兴梨i傾向。
7、常見線程名詞解釋
主線程:JVM調(diào)用程序mian()所產(chǎn)生的線程。
當(dāng)前線程:這個是容易混淆的概念。一般指通過Thread.currentThread()來獲取的進(jìn)程。后臺線程:指為其他線程提供服務(wù)的線程,也稱為守護(hù)線程。JVM的垃圾回收線程就是一個后臺線程。
前臺線程:是指接受后臺線程服務(wù)的線程,其實(shí)前臺后臺線程是聯(lián)系在一起,就像傀儡和幕后操縱者一樣的關(guān)系??苁乔芭_線程、幕后操縱者是后臺線程。由前臺線程創(chuàng)建的線程默認(rèn)也是前臺線程??梢酝ㄟ^isDaemon()和setDaemon()方法來判斷和設(shè)置一個線程是否為后臺線程。
本文出自 “熔 巖” 博客,請務(wù)必保留此出處http://lavasoft.blog.51cto.com/62575/27069
第三篇:多線程編程知識總結(jié)
多線程編程
一、問題的提出
1.1問題的引出
編寫一個耗時的單線程程序:
新建一個基于對話框的應(yīng)用程序SingleThread,在主對話框IDD_SINGLETHREAD_DIALOG添加一個按鈕,ID為IDC_SLEEP_SIX_SECOND,標(biāo)題為“延時6秒”,添加按鈕的響應(yīng)函數(shù),代碼如下:
void CSingleThreadDlg::OnSleepSixSecond(){ Sleep(6000);//延時6秒 } 編譯并運(yùn)行應(yīng)用程序,單擊“延時6秒”按鈕,你就會發(fā)現(xiàn)在這6秒期間程序就象“死機(jī)”一樣,不在響應(yīng)其它消息。為了更好地處理這種耗時的操作,我們有必要學(xué)習(xí)——多線程編程。
1.2多線程概述
進(jìn)程和線程都是操作系統(tǒng)的概念。進(jìn)程是應(yīng)用程序的執(zhí)行實(shí)例,每個進(jìn)程是由私有的虛擬地址空間、代碼、數(shù)據(jù)和其它各種系統(tǒng)資源組成,進(jìn)程在運(yùn)行過程中創(chuàng)建的資源隨著進(jìn)程的終止而被銷毀,所使用的系統(tǒng)資源在進(jìn)程終止時被釋放或關(guān)閉。
線程是進(jìn)程內(nèi)部的一個執(zhí)行單元。系統(tǒng)創(chuàng)建好進(jìn)程后,實(shí)際上就啟動執(zhí)行了該進(jìn)程的主執(zhí)行線程,主執(zhí)行線程以函數(shù)地址形式,比如說main或WinMain函數(shù),將程序的啟動點(diǎn)提供給Windows系統(tǒng)。主執(zhí)行線程終止了,進(jìn)程也就隨之終止。
每一個進(jìn)程至少有一個主執(zhí)行線程,它無需由用戶去主動創(chuàng)建,是由系統(tǒng)自動創(chuàng)建的。用戶根據(jù)需要在應(yīng)用程序中創(chuàng)建其它線程,多個線程并發(fā)地運(yùn)行于同一個進(jìn)程中。一個進(jìn)程中的所有線程都在該進(jìn)程的虛擬地址空間中,共同使用這些虛擬地址空間、全局變量和系統(tǒng)資源,所以線程間的通訊非常方便,多線程技術(shù)的應(yīng)用也較為廣泛。
多線程可以實(shí)現(xiàn)并行處理,避免了某項(xiàng)任務(wù)長時間占用CPU時間。要說明的一點(diǎn)是,對于單處理器(CPU)的,為了運(yùn)行所有這些線程,操作系統(tǒng)為每個獨(dú)立線程安排一些CPU時間,操作系統(tǒng)以輪換方式向線程提供時間片,這就給人一種假象,好象這些線程都在同時運(yùn)行。由此可見,如果兩個非?;钴S的線程為了搶奪對CPU的控制權(quán),在線程切換時會消耗很多的CPU資源,反而會降低系統(tǒng)的性能。這一點(diǎn)在多線程編程時應(yīng)該注意。
Win32 SDK函數(shù)支持進(jìn)行多線程的程序設(shè)計(jì),并提供了操作系統(tǒng)原理中的各種同步、互斥和臨界區(qū)等操作。Visual C++中,使用MFC類庫也實(shí)現(xiàn)了多線程的程序設(shè)計(jì),使得多線程編程更加方便。1.3 Win32 API對多線程編程的支持
Win32 提供了一系列的API函數(shù)來完成線程的創(chuàng)建、掛起、恢復(fù)、終結(jié)以及通信等工作。下面將選取其中的一些重要函數(shù)進(jìn)行說明。
1、HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);該函數(shù)在其調(diào)用進(jìn)程的進(jìn)程空間里創(chuàng)建一個新的線程,并返回已建線程的句柄,其中各參數(shù)說明如下:
lpThreadAttributes:指向一個 SECURITY_ATTRIBUTES 結(jié)構(gòu)的指針,該結(jié)構(gòu)決定了線程的安全屬性,一般置為 NULL;
dwStackSize:指定了線程的堆棧深度,一般都設(shè)置為0;
lpStartAddress:表示新線程開始執(zhí)行時代碼所在函數(shù)的地址,即線程的起始地址。一般情況為(LPTHREAD_START_ROUTINE)ThreadFunc,ThreadFunc 是線程函數(shù)名;
lpParameter:指定了線程執(zhí)行時傳送給線程的32位參數(shù),即線程函數(shù)的參數(shù);
dwCreationFlags:控制線程創(chuàng)建的附加標(biāo)志,可以取兩種值。如果該參數(shù)為0,線程在被創(chuàng)建后就會立即開始執(zhí)行;如果該參數(shù)為CREATE_SUSPENDED,則系統(tǒng)產(chǎn)生線程后,該線程處于掛起狀態(tài),并不馬上執(zhí)行,直至函數(shù)ResumeThread被調(diào)用;
lpThreadId:該參數(shù)返回所創(chuàng)建線程的ID;
如果創(chuàng)建成功則返回線程的句柄,否則返回NULL。
2、DWORD SuspendThread(HANDLE hThread);該函數(shù)用于掛起指定的線程,如果函數(shù)執(zhí)行成功,則線程的執(zhí)行被終止。
3、DWORD ResumeThread(HANDLE hThread);該函數(shù)用于結(jié)束線程的掛起狀態(tài),執(zhí)行線程。
4、VOID ExitThread(DWORD dwExitCode);該函數(shù)用于線程終結(jié)自身的執(zhí)行,主要在線程的執(zhí)行函數(shù)中被調(diào)用。其中參數(shù)dwExitCode用來設(shè)置線程的退出碼。
5、BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);
一般情況下,線程運(yùn)行結(jié)束之后,線程函數(shù)正常返回,但是應(yīng)用程序可以調(diào)用TerminateThread強(qiáng)行終止某一線程的執(zhí)行。各參數(shù)含義如下: hThread:將被終結(jié)的線程的句柄;
dwExitCode:用于指定線程的退出碼。
使用TerminateThread()終止某個線程的執(zhí)行是不安全的,可能會引起系統(tǒng)不穩(wěn)定;雖然該函數(shù)立即終止線程的執(zhí)行,但并不釋放線程所占用的資源。因此,一般不建議使用該函數(shù)。
6、BOOL PostThreadMessage(DWORD idThread,UINT Msg,WPARAM wParam,LPARAM lParam);該函數(shù)將一條消息放入到指定線程的消息隊(duì)列中,并且不等到消息被該線程處理時便返回。idThread:將接收消息的線程的ID;
Msg:指定用來發(fā)送的消息;
wParam:同消息有關(guān)的字參數(shù);
lParam:同消息有關(guān)的長參數(shù);
調(diào)用該函數(shù)時,如果即將接收消息的線程沒有創(chuàng)建消息循環(huán),則該函數(shù)執(zhí)行失敗。
1.4.Win32 API多線程編程例程
例程1 [MultiThread1] 一個簡單的線程。注意事項(xiàng):
? Volatile:關(guān)鍵字:
volatile是要求C++編譯器不要自作聰明的把變量緩沖在寄存器里.因?yàn)樵撟兞靠赡軙灰馔獾男薷摹?多個線程或其他原因)
如從串口讀數(shù)據(jù)的場合,把變量緩沖在寄存器里,下次去讀寄存器就沒有意義了.因?yàn)榇诘臄?shù)據(jù)可能隨時會改變的.加鎖訪問用于多個線程的場合.在進(jìn)入臨界區(qū)時是肯定要加鎖的.volatile也加上,以保證從內(nèi)存中讀取變量的值.? 終止線程:
Windows終止線程運(yùn)行的四種方法 終止線程運(yùn)行
若要終止線程的運(yùn)行,可以使用下面的方法:
? 線程函數(shù)返回(最好使用這種方法)。
? 通過調(diào)用 ExitThread 函數(shù),線程將自行撤消(最好不要使用這種方法)。
? 同一個進(jìn)程或另一個進(jìn)程中的線程調(diào)用 TerminateThread 函數(shù)(應(yīng)該避免使用這種方法)。
? 包含線程的進(jìn)程終止運(yùn)行(應(yīng)該避免使用這種方法)。
下面將介紹終止線程運(yùn)行的方法,并且說明線程終止運(yùn)行時會出現(xiàn)什么情況。
? 線程函數(shù)返回
始終都應(yīng)該將線程設(shè)計(jì)成這樣的形式,即當(dāng)想要線程終止運(yùn)行時,它們就能夠返回。這是確保所有線程資源被正確地清除的唯一辦法。
如果線程能夠返回,就可以確保下列事項(xiàng)的實(shí)現(xiàn):
? 在線程函數(shù)中創(chuàng)建的所有 C++ 對象均將通過它們的撤消函數(shù)正確地撤消。
? 操作系統(tǒng)將正確地釋放線程堆棧使用的內(nèi)存。
? 系統(tǒng)將線程的退出代碼(在線程的內(nèi)核對象中維護(hù))設(shè)置為線程函數(shù)的返回值。
? 系統(tǒng)將遞減線程內(nèi)核對象的使用計(jì)數(shù)。? 使用 ExitThread 函數(shù)
可以讓線程調(diào)用 ExitThread 函數(shù),以便強(qiáng)制線程終止運(yùn)行:
VOID ExitThread(DWORD dwExitCode);
該函數(shù)將終止線程的運(yùn)行,并導(dǎo)致操作系統(tǒng)清除該線程使用的所有操作系統(tǒng)資源。但是,C++ 資源(如 C++ 類對象)將不被撤消。由于這個原因,最好從線程函數(shù)返回,而不是通過調(diào)用 ExitThread 來返回。
當(dāng)然,可以使用 ExitThread 的 dwExitThread 參數(shù)告訴系統(tǒng)將線程的退出代碼設(shè)置為什么。ExitThread 函數(shù)并不返回任何值,因?yàn)榫€程已經(jīng)終止運(yùn)行,不能執(zhí)行更多的代碼。? 使用 TerminateThread 函數(shù)
調(diào)用 TerminateThread 函數(shù)也能夠終止線程的運(yùn)行:
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);
與 ExitThread 不同,ExitThread 總是撤消調(diào)用的線程,而 TerminateThread 能夠撤消任何線程。hThread 參數(shù)用于標(biāo)識被終止運(yùn)行的線程的句柄。當(dāng)線程終止運(yùn)行時,它的退出代碼成為你作為 dwExitCode 參數(shù)傳遞的值。同時,線程的內(nèi)核對象的使用計(jì)數(shù)也被遞減。
注意 TerminateThread 函數(shù)是異步運(yùn)行的函數(shù),也就是說,它告訴系統(tǒng)你想要線程終止運(yùn)行,但是,當(dāng)函數(shù)返回時,不能保證線程被撤消。如果需要確切地知道該線程已經(jīng)終止運(yùn)行,必須調(diào)用 WaitForSingleObject 或者類似的函數(shù),傳遞線程的句柄。
設(shè)計(jì)良好的應(yīng)用程序從來不使用這個函數(shù),因?yàn)楸唤K止運(yùn)行的線程收不到它被撤消的通知。線程不能正確地清除,并且不能防止自己被撤消。
注意 當(dāng)使用返回或調(diào)用 ExitThread 的方法撤消線程時,該線程的內(nèi)存堆棧也被撤消。但是,如果使用 TerminateThread,那么在擁有線程的進(jìn)程終止運(yùn)行之前,系統(tǒng)不撤消該線程的堆棧。Microsoft故意用這種方法來實(shí)現(xiàn) TerminateThread。如果其他仍然正在執(zhí)行的線程要引用強(qiáng)制撤消的線程堆棧上的值,那么其他的線程就會出現(xiàn)訪問違規(guī)的問題。如果將已經(jīng)撤消的線程的堆棧留在內(nèi)存中,那么其他線程就可以繼續(xù)很好地運(yùn)行。
此外,當(dāng)線程終止運(yùn)行時,DLL 通常接收通知。如果使用 TerminateThread 強(qiáng)迫線程終止,DLL 就不接收通知,這能阻止適當(dāng)?shù)那宄?,在進(jìn)程終止運(yùn)行時撤消線程。當(dāng)線程終止運(yùn)行時,會發(fā)生下列操作:
? 線程擁有的所有用戶對象均被釋放。在 Windows 中,大多數(shù)對象是由包含創(chuàng)建這些對象的線程的進(jìn)程擁有的。但是一個線程擁有兩個用戶對象,即窗口和掛鉤。當(dāng)線程終止運(yùn)行時,系統(tǒng)會自動撤消任何窗口,并且卸載線程創(chuàng)建的或安裝的任何掛鉤。其他對象只有在擁有線程的進(jìn)程終止運(yùn)行時才被撤消。
? 線程的退出代碼從 STILL_ACTIVE 改為傳遞給 ExitThread 或 TerminateThread 的代碼。
? 線程內(nèi)核對象的狀態(tài)變?yōu)橐淹ㄖ?/p>
? 如果線程是進(jìn)程中最后一個活動線程,系統(tǒng)也將進(jìn)程視為已經(jīng)終止運(yùn)行。
? 線程內(nèi)核對象的使用計(jì)數(shù)遞減 1。
當(dāng)一個線程終止運(yùn)行時,在與它相關(guān)聯(lián)的線程內(nèi)核對象的所有未結(jié)束的引用關(guān)閉之前,該內(nèi)核對象不會自動被釋放。
一旦線程不再運(yùn)行,系統(tǒng)中就沒有別的線程能夠處理該線程的句柄。然而別的線程可以調(diào)用 GetExitcodeThread 來檢查由 hThread 標(biāo)識的線程是否已經(jīng)終止運(yùn)行。如果它已經(jīng)終止運(yùn)行,則確定它的退出代碼:
BOOL GetExitCodeThread(HANDLE hThread, PDOWRD pdwExitCode);退出代碼的值在 pdwExitCode 指向的 DWORD 中返回。如果調(diào)用 GetExitCodeThread 時線程尚未終止運(yùn)行,該函數(shù)就用 STILL_ACTIVE 標(biāo)識符(定義為 0x103)填入 DWORD。如果該函數(shù)運(yùn)行成功,便返回 TRUE。
? 線程的定義:
例程2[MultiThread2] 傳送一個一個整型的參數(shù)到一個線程中,以及如何等待一個線程完成處理。
DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);
hHandle:為要監(jiān)視的對象(一般為同步對象,也可以是線程)的句柄;
dwMilliseconds:為hHandle對象所設(shè)置的超時值,單位為毫秒;
當(dāng)在某一線程中調(diào)用該函數(shù)時,線程暫時掛起,系統(tǒng)監(jiān)視hHandle所指向的對象的狀態(tài)。如果在掛起的dwMilliseconds毫秒內(nèi),線程所等待的對象變?yōu)橛行盘枲顟B(tài),則該函數(shù)立即返回;如果超時時間已經(jīng)到達(dá)dwMilliseconds毫秒,但hHandle所指向的對象還沒有變成有信號狀態(tài),函數(shù)照樣返回。參數(shù)dwMilliseconds有兩個具有特殊意義的值:0和INFINITE。若為0,則該函數(shù)立即返回;若為INFINITE,則線程一直被掛起,直到hHandle所指向的對象變?yōu)橛行盘枲顟B(tài)時為止。
例程3[MultiThread3] 傳送一個結(jié)構(gòu)體給一個線程函數(shù),可以通過傳送一個指向結(jié)構(gòu)體的指針參數(shù)來完成。補(bǔ)充一點(diǎn):如果你在void CMultiThread3Dlg::OnStart()函數(shù)中添加/* */語句,編譯運(yùn)行你就會發(fā)現(xiàn)進(jìn)度條不進(jìn)行刷新,主線程也停止了反應(yīng)。什么原因呢?這是因?yàn)閃aitForSingleObject函數(shù)等待子線程(ThreadFunc)結(jié)束時,導(dǎo)致了線程死鎖。因?yàn)閃aitForSingleObject函數(shù)會將主線程掛起(任何消息都得不到處理),而子線程ThreadFunc正在設(shè)置進(jìn)度條,一直在等待主線程將刷新消息處理完畢返回才會檢測通知事件。這樣兩個線程都在互相等待,死鎖發(fā)生了,編程時應(yīng)注意避免。
例程4[MultiThread4] 測試在Windows下最多可創(chuàng)建線程的數(shù)目。
二、MFC中的多線程開發(fā)
2.1 MFC對多線程編程的支持
MFC中有兩類線程,分別稱之為工作者線程和用戶界面線程。二者的主要區(qū)別在于工作者線程沒有消息循環(huán),而用戶界面線程有自己的消息隊(duì)列和消息循環(huán)。
工作者線程沒有消息機(jī)制,通常用來執(zhí)行后臺計(jì)算和維護(hù)任務(wù),如冗長的計(jì)算過程,打印機(jī)的后臺打印等。用戶界面線程一般用于處理獨(dú)立于其他線程執(zhí)行之外的用戶輸入,響應(yīng)用戶及系統(tǒng)所產(chǎn)生的事件和消息等。但對于Win32的API編程而言,這兩種線程是沒有區(qū)別的,它們都只需線程的啟動地址即可啟動線程來執(zhí)行任務(wù)。
在MFC中,一般用全局函數(shù)AfxBeginThread()來創(chuàng)建并初始化一個線程的運(yùn)行,該函數(shù)有兩種重載形式,分別用于創(chuàng)建工作者線程和用戶界面線程。兩種重載函數(shù)原型和參數(shù)分別說明如下:
(1)CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,LPVOID pParam,nPriority=THREAD_PRIORITY_NORMAL,UINT nStackSize=0,DWORD dwCreateFlags=0,LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);
PfnThreadProc:指向工作者線程的執(zhí)行函數(shù)的指針,線程函數(shù)原型必須聲明如下: UINT ExecutingFunction(LPVOID pParam);請注意,ExecutingFunction()應(yīng)返回一個UINT類型的值,用以指明該函數(shù)結(jié)束的原因。一般情況下,返回0表明執(zhí)行成功。
pParam:傳遞給線程函數(shù)的一個32位參數(shù),執(zhí)行函數(shù)將用某種方式解釋該值。它可以是數(shù)值,或是指向一個結(jié)構(gòu)的指針,甚至可以被忽略;
nPriority:線程的優(yōu)先級。如果為0,則線程與其父線程具有相同的優(yōu)先級;
nStackSize:線程為自己分配堆棧的大小,其單位為字節(jié)。如果nStackSize被設(shè)為0,則線程的堆棧被設(shè)置成與父線程堆棧相同大??; dwCreateFlags:如果為0,則線程在創(chuàng)建后立刻開始執(zhí)行。如果為CREATE_SUSPEND,則線程在創(chuàng)建后立刻被掛起;
lpSecurityAttrs:線程的安全屬性指針,一般為NULL;
(2)CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass,int nPriority=THREAD_PRIORITY_NORMAL,UINT nStackSize=0,DWORD dwCreateFlags=0,LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);
pThreadClass 是指向 CWinThread 的一個導(dǎo)出類的運(yùn)行時類對象的指針,該導(dǎo)出類定義了被創(chuàng)建的用戶界面線程的啟動、退出等;其它參數(shù)的意義同形式1。使用函數(shù)的這個原型生成的線程也有消息機(jī)制,在以后的例子中我們將發(fā)現(xiàn)同主線程的機(jī)制幾乎一樣。下面對CWinThread類的數(shù)據(jù)成員及常用函數(shù)進(jìn)行簡要說明。
? ? ? m_hThread:當(dāng)前線程的句柄;
m_nThreadID:當(dāng)前線程的ID;
m_pMainWnd:指向應(yīng)用程序主窗口的指針
virtual BOOL CWinThread::InitInstance();重載該函數(shù)以控制用戶界面線程實(shí)例的初始化。初始化成功則返回非0值,否則返回0。用戶界面線程經(jīng)常重載該函數(shù),工作者線程一般不使用InitInstance()。
virtual int CWinThread::ExitInstance();在線程終結(jié)前重載該函數(shù)進(jìn)行一些必要的清理工作。該函數(shù)返回線程的退出碼,0表示執(zhí)行成功,非0值用來標(biāo)識各種錯誤。同InitInstance()成員函數(shù)一樣,該函數(shù)也只適用于用戶界面線程。
2.2 MFC多線程編程實(shí)例
例程5 MultiThread5 為了與Win32 API對照,使用MFC 類庫編程實(shí)現(xiàn)例程3 MultiThread3。
例程6 MultiThread6[用戶界面線程] ? 創(chuàng)建用戶界面線程的步驟:
1.使用ClassWizard創(chuàng)建類CWinThread的派生類(以CUIThread類為例)class CUIThread : public CWinThread { DECLARE_DYNCREATE(CUIThread)protected: CUIThread();// protected constructor used by dynamic creation
// Attributes public: // Operations public:
// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CUIThread)public: virtual BOOL InitInstance();virtual int ExitInstance();//}}AFX_VIRTUAL // Implementation protected: virtual ~CUIThread();// Generated message map functions //{{AFX_MSG(CUIThread)
// NOTE-the ClassWizard will add and remove member functions here.//}}AFX_MSG
DECLARE_MESSAGE_MAP()};
2.重載函數(shù)InitInstance()和ExitInstance()。BOOL CUIThread::InitInstance(){ CFrameWnd* wnd=new CFrameWnd;wnd->Create(NULL,“UI Thread Window”);wnd->ShowWindow(SW_SHOW);wnd->UpdateWindow();m_pMainWnd=wnd;return TRUE;}
3.創(chuàng)建新的用戶界面線程 void CUIThreadDlg::OnButton1(){
}
請注意以下兩點(diǎn):
A、在UIThreadDlg.cpp的開頭加入語句: #include “UIThread.h” B、把UIThread.h中類CUIThread()的構(gòu)造函數(shù)的特性由 protected 改為 public。CUIThread* pThread=new CUIThread();pThread->CreateThread();
用戶界面線程的執(zhí)行次序與應(yīng)用程序主線程相同,首先調(diào)用用戶界面線程類的InitInstance()函數(shù),如果返回TRUE,繼續(xù)調(diào)用線程的Run()函數(shù),該函數(shù)的作用是運(yùn)行一個標(biāo)準(zhǔn)的消息循環(huán),并且當(dāng)收到WM_QUIT消息后中斷,在消息循環(huán)過程中,Run()函數(shù)檢測到線程空閑時(沒有消息),也將調(diào)用OnIdle()函數(shù),最后Run()函數(shù)返回,MFC調(diào)用ExitInstance()函數(shù)清理資源。
你可以創(chuàng)建一個沒有界面而有消息循環(huán)的線程,例如:你可以從CWinThread派生一個新類,在InitInstance函數(shù)中完成某項(xiàng)任務(wù)并返回FALSE,這表示僅執(zhí)行InitInstance函數(shù)中的任務(wù)而不執(zhí)行消息循環(huán),你可以通過這種方法,完成一個工作者線程的功能。
三、線程間通訊
3.1通訊方式
一般而言,應(yīng)用程序中的一個次要線程總是為主線程執(zhí)行特定的任務(wù),這樣,主線程和次要線程間必定有一個信息傳遞的渠道,也就是主線程和次要線程間要進(jìn)行通信。這種線程間的通信不但是難以避免的,而且在多線程編程中也是復(fù)雜和頻繁的,下面將進(jìn)行說明。
3.1.1使用全局變量進(jìn)行通信
由于屬于同一個進(jìn)程的各個線程共享操作系統(tǒng)分配該進(jìn)程的資源,故解決線程間通信最簡單的一種方法是使用全局變量。對于標(biāo)準(zhǔn)類型的全局變量,建議使用volatile 修飾符,它告訴編譯器無需對該變量作任何的優(yōu)化,即無需將它放到一個寄存器中,并且該值可被外部改變。如果線程間所需傳遞的信息較復(fù)雜,可以定義一個結(jié)構(gòu),通過傳遞指向該結(jié)構(gòu)的指針進(jìn)行傳遞信息。
3.1.2使用自定義消息
可以在一個線程的執(zhí)行函數(shù)中向另一個線程發(fā)送自定義的消息來達(dá)到通信的目的。一個線程向另外一個線程發(fā)送消息是通過操作系統(tǒng)實(shí)現(xiàn)的。利用Windows操作系統(tǒng)的消息驅(qū)動機(jī)制,當(dāng)一個線程發(fā)出一條消息時,操作系統(tǒng)首先接收到該消息,然后把該消息轉(zhuǎn)發(fā)給目標(biāo)線程,接收消息的線程必須已經(jīng)建立了消息循環(huán)。
3.2例程
例程GlobalObjectTest 該例程演示了如何利用全局變量進(jìn)行通信
例程7[MultiThread7] 該例程演示了如何使用自定義消息進(jìn)行線程間通信。首先,主線程向CCalculateThread線程發(fā)送消息WM_CALCULATE,CCalculateThread線程收到消息后進(jìn)行計(jì)算,再向主線程發(fā)送WM_DISPLAY消息,主線程收到該消息后顯示計(jì)算結(jié)果。步驟:
四、線程的同步
4.1基本概念
雖然多線程能給我們帶來好處,但是也有不少問題需要解決。例如,對于像磁盤驅(qū)動器這樣獨(dú)占性系統(tǒng)資源,由于線程可以執(zhí)行進(jìn)程的任何代碼段,且線程的運(yùn)行是由系統(tǒng)調(diào)度自動完成的,具有一定的不確定性,因此就有可能出現(xiàn)兩個線程同時對磁盤驅(qū)動器進(jìn)行操作,從而出現(xiàn)操作錯誤;又例如,對于銀行系統(tǒng)的計(jì)算機(jī)來說,可能使用一個線程來更新其用戶數(shù)據(jù)庫,而用另外一個線程來讀取數(shù)據(jù)庫以響應(yīng)儲戶的需要,極有可能讀數(shù)據(jù)庫的線程讀取的是未完全更新的數(shù)據(jù)庫,因?yàn)榭赡茉谧x的時候只有一部分?jǐn)?shù)據(jù)被更新過。
使隸屬于同一進(jìn)程的各線程協(xié)調(diào)一致地工作稱為線程的同步。MFC提供了多種同步對象,下面只介紹最常用的四種:
臨界區(qū)(CCriticalSection)
事件(CEvent)
互斥量(CMutex)
信號量(CSemaphore)
通過這些類,可以比較容易地做到線程同步。
4.2使用 CCriticalSection 類
當(dāng)多個線程訪問一個獨(dú)占性共享資源時,可以使用“臨界區(qū)”對象。任一時刻只有一個線程可以擁有臨界區(qū)對象,擁有臨界區(qū)的線程可以訪問被保護(hù)起來的資源或代碼段,其他希望進(jìn)入臨界區(qū)的線程將被掛起等待,直到擁有臨界區(qū)的線程放棄臨界區(qū)時為止,這樣就保證了不會在同一時刻出現(xiàn)多個線程訪問共享資源。
CCriticalSection類的用法非常簡單,步驟如下:
1.定義CCriticalSection類的一個全局對象(以使各個線程均能訪問),如CCriticalSection critical_section;
2.在訪問需要保護(hù)的資源或代碼之前,調(diào)用CCriticalSection類的成員Lock()獲得臨界區(qū)對象: critical_section.Lock();3.在線程中調(diào)用該函數(shù)來使線程獲得它所請求的臨界區(qū)。如果此時沒有其它線程占有臨界區(qū)對象,則調(diào)用Lock()的線程獲得臨界區(qū);否則,線程將被掛起,并放入到一個系統(tǒng)隊(duì)列中等待,直到當(dāng)前擁有臨界區(qū)的線程釋放了臨界區(qū)時為止。
4.訪問臨界區(qū)完畢后,使用CCriticalSection的成員函數(shù)Unlock()來釋放臨界區(qū):critical_section.Unlock();通俗講,就是線程A執(zhí)行到critical_section.Lock();語句時,如果其它線程(B)正在執(zhí)行critical_section.Lock();語句后且critical_section.Unlock();語句前的語句時,線程A就會等待,直到線程B執(zhí)行完critical_section.Unlock();語句,線程A才會繼續(xù)執(zhí)行。
例程8 MultiThread8 4.3使用 CEvent 類
CEvent 類提供了對事件的支持。事件是一個允許一個線程在某種情況發(fā)生時,喚醒另外一個線程的同步對象。例如在某些網(wǎng)絡(luò)應(yīng)用程序中,一個線程(記為A)負(fù)責(zé)監(jiān)聽通訊端口,另外一個線程(記為B)負(fù)責(zé)更新用戶數(shù)據(jù)。通過使用CEvent 類,線程A可以通知線程B何時更新用戶數(shù)據(jù)。每一個CEvent 對象可以有兩種狀態(tài):有信號狀態(tài)和無信號狀態(tài)。線程監(jiān)視位于其中的CEvent 類對象的狀態(tài),并在相應(yīng)的時候采取相應(yīng)的操作。
在MFC中,CEvent 類對象有兩種類型:人工事件和自動事件。一個自動CEvent 對象在被至少一個線程釋放后會自動返回到無信號狀態(tài);而人工事件對象獲得信號后,釋放可利用線程,但直到調(diào)用成員函數(shù)ReSetEvent()才將其設(shè)置為無信號狀態(tài)。在創(chuàng)建CEvent 類的對象時,默認(rèn)創(chuàng)建的是自動事件。CEvent 類的各成員函數(shù)的原型和參數(shù)說明如下:
1、CEvent(BOOL bInitiallyOwn=FALSE,BOOL bManualReset=FALSE,LPCTSTR lpszName=NULL,LPSECURITY_ATTRIBUTES lpsaAttribute=NULL);bInitiallyOwn:指定事件對象初始化狀態(tài),TRUE為有信號,F(xiàn)ALSE為無信號;
bManualReset:指定要創(chuàng)建的事件是屬于人工事件還是自動事件。TRUE為人工事件,F(xiàn)ALSE為自動事件;
后兩個參數(shù)一般設(shè)為NULL,在此不作過多說明。
2、BOOL CEvent::SetEvent();
將 CEvent 類對象的狀態(tài)設(shè)置為有信號狀態(tài)。如果事件是人工事件,則 CEvent 類對象保持為有信號狀態(tài),直到調(diào)用成員函數(shù)ResetEvent()將 其重新設(shè)為無信號狀態(tài)時為止。如果CEvent 類對象為自動事件,則在SetEvent()將事件設(shè)置為有信號狀態(tài)后,CEvent 類對象由系統(tǒng)自動重置為無信號狀態(tài)。
如果該函數(shù)執(zhí)行成功,則返回非零值,否則返回零。
3、BOOL CEvent::ResetEvent();
該函數(shù)將事件的狀態(tài)設(shè)置為無信號狀態(tài),并保持該狀態(tài)直至SetEvent()被調(diào)用時為止。由于自動事件是由系統(tǒng)自動重置,故自動事件不需要調(diào)用該函數(shù)。如果該函數(shù)執(zhí)行成功,返回非零值,否則返回零。一般通過調(diào)用WaitForSingleObject函數(shù)來監(jiān)視事件狀態(tài)。前面已經(jīng)介紹了該函數(shù)。由于語言描述的原因,CEvent 類的理解確實(shí)有些難度,只要通過下面例程,多看幾遍就可理解。例程9 MultiThread9 仔細(xì)分析這兩個線程函數(shù), 就會正確理解CEvent 類。線程WriteD執(zhí)行到 WaitForSingleObject(eventWriteD.m_hObject,INFINITE);處等待,直到事件eventWriteD為有信號該線程才往下執(zhí)行,因?yàn)閑ventWriteD對象是自動事件,則當(dāng)WaitForSingleObject()返回時,系統(tǒng)自動把eventWriteD對象重置為無信號狀態(tài)。
4.4使用CMutex 類
互斥對象與臨界區(qū)對象很像.互斥對象與臨界區(qū)對象的不同在于:互斥對象可以在進(jìn)程間使用,而臨界區(qū)對象只能在同一進(jìn)程的各線程間使用。當(dāng)然,互斥對象也可以用于同一進(jìn)程的各個線程間,但是在這種情況下,使用臨界區(qū)會更節(jié)省系統(tǒng)資源,更有效率。
4.5使用CSemaphore 類
當(dāng)需要一個計(jì)數(shù)器來限制可以使用某個線程的數(shù)目時,可以使用“信號量”對象。CSemaphore 類的對象保存了對當(dāng)前訪問某一指定資源的線程的計(jì)數(shù)值,該計(jì)數(shù)值是當(dāng)前還可以使用該資源的線程的數(shù)目。如果這個計(jì)數(shù)達(dá)到了零,則所有對這個CSemaphore 類對象所控制的資源的訪問嘗試都被放入到一個隊(duì)列中等待,直到超時或計(jì)數(shù)值不為零時為止。一個線程被釋放已訪問了被保護(hù)的資源時,計(jì)數(shù)值減1;一個線程完成了對被控共享資源的訪問時,計(jì)數(shù)值增1。這個被CSemaphore 類對象所控制的資源可以同時接受訪問的最大線程數(shù)在該對象的構(gòu)建函數(shù)中指定。
CSemaphore 類的構(gòu)造函數(shù)原型及參數(shù)說明如下:
CSemaphore(LONG lInitialCount=1,LONG lMaxCount=1,LPCTSTR pstrName=NULL,LPSECURITY_ATTRIBUTES lpsaAttributes=NULL);lInitialCount:信號量對象的初始計(jì)數(shù)值,即可訪問線程數(shù)目的初始值;
lMaxCount:信號量對象計(jì)數(shù)值的最大值,該參數(shù)決定了同一時刻可訪問由信號量保護(hù)的資源的線程最大數(shù)目;
后兩個參數(shù)在同一進(jìn)程中使用一般為NULL,不作過多討論;
在用CSemaphore 類的構(gòu)造函數(shù)創(chuàng)建信號量對象時要同時指出允許的最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)。一般是將當(dāng)前可用資源計(jì)數(shù)設(shè)置為最大資源計(jì)數(shù),每增加一個線程對共享資源的訪問,當(dāng)前可用資源計(jì)數(shù)就會減1,只要當(dāng)前可用資源計(jì)數(shù)是大于0的,就可以發(fā)出信號量信號。但是當(dāng)前可用計(jì)數(shù)減小到0時,則說明當(dāng)前占用資源的線程數(shù)已經(jīng)達(dá)到了所允許的最大數(shù)目,不能再允許其它線程的進(jìn)入,此時的信號量信號將無法發(fā)出。線程在處理完共享資源后,應(yīng)在離開的同時通過ReleaseSemaphore()函數(shù)將當(dāng)前可用資源數(shù)加1。例程10 MultiThread10 為了文件中能夠正確使用同步類,在文件開頭添加: #include “afxmt.h” 定義信號量對象和一個字符數(shù)組,為了能夠在不同線程間使用,定義為全局變量:CSemaphore semaphoreWrite(2,2);//資源最多訪問線程2個,當(dāng)前可訪問線程數(shù)2個
在信號量對象有信號的狀態(tài)下,線程執(zhí)行到WaitForSingleObject語句處繼續(xù)執(zhí)行,同時可用線程數(shù)減1;若線程執(zhí)行到WaitForSingleObject語句時信號量對象無信號,線程就在這里等待,直到信號量對象有信號線程才往下執(zhí)行。
第四篇:多線程實(shí)驗(yàn)報(bào)告
寧波工程學(xué)院電信學(xué)院計(jì)算機(jī)教研室
實(shí)驗(yàn)報(bào)告
課程名稱: Java 2 姓 名: *** 實(shí)驗(yàn)項(xiàng)目: 多線程實(shí)驗(yàn) 學(xué) 號: **** 指導(dǎo)教師: **** 班 級: **** 實(shí)驗(yàn)位置: 電信樓機(jī)房 日 期:
一、實(shí)驗(yàn)?zāi)康?/p>
1、掌握多線程編程的特點(diǎn)和工作原理;
2、掌握編寫線程程序的方法
3、了解線程的調(diào)度和執(zhí)行過程
4、掌握線程同步機(jī)理
二、實(shí)驗(yàn)環(huán)境
windows記事本,java jdk 1.60版本,cmd命令運(yùn)行窗口
三、實(shí)驗(yàn)內(nèi)容 實(shí)驗(yàn)一:
應(yīng)用Java中線程的概念寫一個Java程序(包括一個測試線程程序類TestThread,一個Thread類的子類PrintThread)。在測試程序中用子類PrintThread創(chuàng)建2個線程,使得其中一個線程運(yùn)行時打印10次“線程1正在運(yùn)行”,另一個線程運(yùn)行時打印5次“線程2正在運(yùn)行
源程序:
public class A { public static void main(String args[]){
Test1 A1;
Test2 A2;
A1=new Test1();
A2=new Test2();
A1.start();
A2.start();} } class PrintThread extends Thread { } class Test1 extends PrintThread { public void run(){
for(int i=1;i<=10;i++)
{
System.out.println(“線程1正在運(yùn)行!”);
} } } class Test2 extends PrintThread { public void run(){
for(int i=1;i<=5;i++)
{
System.out.println(“線程2正在運(yùn)行!”);
} } } 運(yùn)行結(jié)果:
實(shí)驗(yàn)二:
將上述程序用Runnable接口改寫,并上機(jī)驗(yàn)證源程序 public class D { public static void main(String args[]){
Move move=new Move();
move.test1.start();
move.test2.start();} } class Move implements Runnable { Thread test1,test2;Move(){
test1=new Thread(this);
test1.setName(“線程1正在運(yùn)行!”);
test2=new Thread(this);
test2.setName(“線程2正在運(yùn)行!”);} public void run(){
if(Thread.currentThread()==test1)
{
for(int i=1;i<=10;i++)
{
System.out.println(test1.getName());
} } } else { for(int i=1;i<=5;i++){
System.out.println(test2.getName());} } 運(yùn)行結(jié)果:
實(shí)驗(yàn)三:
import java.awt.*;import java.awt.event.*;public class E
{ public static void main(String args[])
{ new FrameMoney();
} } class FrameMoney extends Frame implements Runnable,ActionListener { int money=100;
TextArea text1,text2;
Thread 會計(jì),出納;
int weekDay;
Button start=new Button(“開始演示”);
FrameMoney()
{ 會計(jì)=new Thread(this);
出納=new Thread(this);
text1=new TextArea(12,15);
text2=new TextArea(12,15);
setLayout(new FlowLayout());
add(start);
add(text1);
add(text2);
setVisible(true);
setSize(360,300);
validate();
addWindowListener(new WindowAdapter()
{ public void windowClosing(WindowEvent e)
{System.exit(0);
}
});
start.addActionListener(this);
}
public void actionPerformed(ActionEvent e)
{ if(!(出納.isAlive()))
{ 會計(jì)=new Thread(this);
出納=new Thread(this);
}
try
{ 會計(jì).start();
出納.start();
}
catch(Exception exp){}
}
public synchronized void 存取(int number)//存取方法
{ if(Thread.currentThread()==會計(jì))
{ text1.append(“今天是星期”+weekDay+“n”);
for(int i=1;i<=3;i++)//會計(jì)使用存取方法存入90元,存入30元,稍歇一下
{ money=money+number;
//這時出納仍不能使用存取方法
try { Thread.sleep(1000);//因?yàn)闀?jì)還沒使用完存取方法
}
catch(InterruptedException e){}
text1.append(“帳上有”+money+“萬n”);
}
}
else if(Thread.currentThread()==出納)
{ text2.append(“今天是星期 ”+weekDay+“n”);
for(int i=1;i<=2;i++)//出納使用存取方法取出30元,取出15元,稍歇一下
{ money=money-number/2;
//這時會計(jì)仍不能使用存取方法
try { Thread.sleep(1000);//因?yàn)槌黾{還沒使用完存取方法
}
catch(InterruptedException e){}
text2.append(“帳上有”+money+“萬n”);
}
}
}
public void run()
{ if(Thread.currentThread()==會計(jì)||Thread.currentThread()==出納)
{ for(int i=1;i<=3;i++)//從周一到周三會計(jì)和出納都要使用帳本
{ weekDay=i;
存取(30);
}
}
} }
運(yùn)行結(jié)果:
}
四、實(shí)驗(yàn)心得與小結(jié)
通過本次實(shí)驗(yàn),基本了解了線程的概念,作用,方法以及使用規(guī)則。1.首先:java 程序是建立在線程之上的。.2.創(chuàng)建線程必須繼承 Thread class 它已經(jīng)為線程的創(chuàng)建和運(yùn)行做了必要的配置。run是線程就重要的方法。你必須覆寫這個方法達(dá)到你想要的目的。3.run方法所包含的代碼就是和其他線程同時運(yùn)行的代碼以達(dá)到同一時刻運(yùn)行多段代碼的目的。當(dāng)終止了 run以后。這個線程也就結(jié)束了。調(diào)用線程的 start方法才會執(zhí)行 run方法。
4.線程的生命周期:新建——Thread.State.NEW:當(dāng)一個 Thread 類或者其子類的對象被聲明并創(chuàng)建時,新的線程對象處于新建狀態(tài),此時它已經(jīng)有了相應(yīng)的內(nèi)存空間和其他資源start方法尚未被調(diào)整用就緒可執(zhí)行狀態(tài)——Thread.State.RUNNABLE:處于新建狀態(tài)的線程被啟動后,將進(jìn)入線程隊(duì)列排隊(duì),這個時候具備了運(yùn)行的條件,一旦輪到 CPU 的時候,就可以脫離創(chuàng)建它的主線程獨(dú)立開始自己的生命周期運(yùn)行:就緒的線程被調(diào)度進(jìn)入運(yùn)行狀態(tài),每一個 Thread 類及其子類的對象都有一個重要的run方法,當(dāng)線程對象被調(diào)度執(zhí)行的時候,它將自動調(diào)用本對象的 run方法,從第一句代碼開始執(zhí)行。
第五篇:嵌入式多線程 實(shí)習(xí)總結(jié)(有感想)
解壓應(yīng)用程序以及多線程應(yīng)用程序設(shè)計(jì)
實(shí)習(xí)過程
首先完成上次實(shí)習(xí)沒有完成的解壓應(yīng)用程序的部分。設(shè)置好宿主機(jī)和目標(biāo)機(jī)的IP地址后,運(yùn)行FTP軟件。將壓縮包從右側(cè)的宿主機(jī)本地目錄“拖到”左側(cè)的目標(biāo)機(jī)目錄中。最后在超級終端上完成解壓。
其次完成多線程的部分,運(yùn)行虛擬機(jī)后,步驟如下:
1、掛載NFS服務(wù)。
系統(tǒng)設(shè)置部分需要完成關(guān)閉防火墻,設(shè)置宿主機(jī)和目標(biāo)機(jī)IP(需在一個網(wǎng)段
內(nèi)),配置NFS服務(wù)器。之后:service nfs start。啟動。
掛載NFS時候出現(xiàn)了問題。當(dāng)設(shè)置宿主機(jī)IP為192.168.1.155之后,在虛擬機(jī)的LINUX終端里mount了192.168.1.155(也就是自己掛載自己),然后總感覺不對,鼓搗了半天,又在超級終端里ifconfig之后出現(xiàn)了三個IP地址,第一個是inet addr,第二個是broadcast,第三個是子網(wǎng)掩碼,但是我當(dāng)時沒看懂第二個地址,于是又把宿主機(jī)的IP設(shè)置為了192.168.1.255。老師一說才想起來計(jì)算機(jī)網(wǎng)絡(luò)課上講的,C類的網(wǎng)絡(luò)地址,后8位若為全1,應(yīng)該是廣播地址才對。反正這塊亂了。
分析后,覺得主要原因還是因?yàn)閷燧d的深層含義不懂,沒明白其實(shí)是目標(biāo)機(jī)
想要宿主機(jī)里的東西,所以要從超級終端里掛載host下的目錄。
最終完成掛載。
Mount –t nfs 192.168.0.2:/arm2410cl/ /mnt/nfs(老師說這里直接寫/mnt不好,會覆蓋掉mnt目錄,如果以后要掛載其他的應(yīng)用,就不好弄了。)
2、第一步成功后,在超級終端上cd arm2410cl/exp/basic/02_pthread。成功進(jìn)入,make
語句后,用命令:./pthread成功運(yùn)行。
3、在虛擬機(jī)的LINUX終端上,也進(jìn)入了arm2410cl/exp/basic/01_hello,但是不能運(yùn)行
hello,用gcc hello.c –o hello之后,./hello就能運(yùn)行了。用這個方法,完成02_pthread,發(fā)現(xiàn)gcc提示幾個相似錯誤,都跟main函數(shù)里的一個函數(shù)有關(guān)。這塊還沒弄懂為什么。準(zhǔn)備周四過去再研究研究。這塊是重點(diǎn)。
實(shí)習(xí)總結(jié)
總體我覺得我們班同學(xué)實(shí)習(xí)的氛圍非常好,沒有人開小差,每個人都很認(rèn)真的在弄自己的東西,不會的就趕緊問旁邊的人,這次實(shí)習(xí)的內(nèi)容并不多,但是確實(shí)很難理解。多線程的部分主要就是體會什么是交叉編譯,實(shí)習(xí)指導(dǎo)書里關(guān)于多線程的相關(guān)介紹好多,需要下來仔細(xì)研究跟體會,basic里面還有好多代碼,也可以都試試和看看。