第一篇:Java多線程編程總結(jié)
Java多線程編程總結(jié)
2007-05-17 11:21:59 標(biāo)簽:java 多線程
原創(chuàng)作品,允許轉(zhuǎn)載,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必以超鏈接形式標(biāo)明文章 原始出處、作者信息和本聲明。否則將追究法律責(zé)任。http://lavasoft.blog.51cto.com/62575/27069
Java多線程編程總結(jié)
下面是Java線程系列博文的一個(gè)編目:
Java線程:概念與原理 Java線程:創(chuàng)建與啟動(dòng)
Java線程:線程棧模型與線程的變量 Java線程:線程狀態(tài)的轉(zhuǎn)換 Java線程:線程的同步與鎖 Java線程:線程的交互 Java線程:線程的調(diào)度-休眠 Java線程:線程的調(diào)度-優(yōu)先級(jí) 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線程:新特征-信號(hào)量 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)容,與時(shí)俱進(jìn),跟上Java發(fā)展的步伐。----
一、認(rèn)識(shí)多任務(wù)、多進(jìn)程、單線程、多線程 要認(rèn)識(shí)多線程就要從操作系統(tǒng)的原理說(shuō)起。
以前古老的DOS操作系統(tǒng)(V 6.22)是單任務(wù)的,還沒(méi)有線程的概念,系統(tǒng)在每次只能做一件事情。比如你在copy東西的時(shí)候不能rename文件名。為了提高系統(tǒng)的利用效率,采用批處理來(lái)批量執(zhí)行任務(wù)。
現(xiàn)在的操作系統(tǒng)都是多任務(wù)操作系統(tǒng),每個(gè)運(yùn)行的任務(wù)就是操作系統(tǒng)所做的一件事情,比如你在聽(tīng)歌的同時(shí)還在用MSN和好友聊天。聽(tīng)歌和聊天就是兩個(gè)任務(wù),這個(gè)兩個(gè)任務(wù)是“同時(shí)”進(jìn)行的。一個(gè)任務(wù)一般對(duì)應(yīng)一個(gè)進(jìn)程,也可能包含好幾個(gè)進(jìn)程。比如運(yùn)行的MSN就對(duì)應(yīng)一個(gè)MSN的進(jìn)程,如果你用的是windows系統(tǒng),你就可以在任務(wù)管理器中看到操作系統(tǒng)正在運(yùn)行的進(jìn)程信息。
一般來(lái)說(shuō),當(dāng)運(yùn)行一個(gè)應(yīng)用程序的時(shí)候,就啟動(dòng)了一個(gè)進(jìn)程,當(dāng)然有些會(huì)啟動(dòng)多個(gè)進(jìn)程。啟動(dòng)進(jìn)程的時(shí)候,操作系統(tǒng)會(huì)為進(jìn)程分配資源,其中最主要的資源是內(nèi)存空間,因?yàn)槌绦蚴窃趦?nèi)存中運(yùn)行的。在進(jìn)程中,有些程序流程塊是可以亂序執(zhí)行的,并且這個(gè)代碼塊可以同時(shí)被多次執(zhí)行。實(shí)際上,這樣的代碼塊就是線程體。線程是進(jìn)程中亂序執(zhí)行的代碼流程。當(dāng)多個(gè)線程同時(shí)運(yùn)行的時(shí)候,這樣的執(zhí)行模式成為并發(fā)執(zhí)行。
多線程的目的是為了最大限度的利用CPU資源。
Java編寫程序都運(yùn)行在在Java虛擬機(jī)(JVM)中,在JVM的內(nèi)部,程序的多任務(wù)是通過(guò)線程來(lái)實(shí)現(xiàn)的。每用java命令啟動(dòng)一個(gè)java應(yīng)用程序,就會(huì)啟動(dòng)一個(gè)JVM進(jìn)程。在同一個(gè)JVM進(jìn)程中,有且只有一個(gè)進(jìn)程,就是它自己。在這個(gè)JVM環(huán)境中,所有程序代碼的運(yùn)行都是以線程來(lái)運(yùn)行。
一般常見(jiàn)的Java應(yīng)用程序都是單線程的。比如,用java命令運(yùn)行一個(gè)最簡(jiǎn)單的HelloWorld的Java應(yīng)用程序時(shí),就啟動(dòng)了一個(gè)JVM進(jìn)程,JVM找到程序程序的入口點(diǎn)main(),然后運(yùn)行main()方法,這樣就產(chǎn)生了一個(gè)線程,這個(gè)線程稱之為主線程。當(dāng)main方法結(jié)束后,主線程運(yùn)行完成。JVM進(jìn)程也隨即退出。
對(duì)于一個(gè)進(jìn)程中的多個(gè)線程來(lái)說(shuō),多個(gè)線程共享進(jìn)程的內(nèi)存塊,當(dāng)有新的線程產(chǎn)生的時(shí)候,操作系統(tǒng)不分配新的內(nèi)存,而是讓新線程共享原有的進(jìn)程塊的內(nèi)存。因此,線程間的通信很容易,速度也很快。不同的進(jìn)程因?yàn)樘幱诓煌膬?nèi)存塊,因此進(jìn)程之間的通信相對(duì)困難。
實(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ī)制為多個(gè)線程分配CPU的使用權(quán)。調(diào)度的模式有兩種:分時(shí)調(diào)度和搶占式調(diào)度。分時(shí)調(diào)度是所有線程輪流獲得CPU使用權(quán),并平均分配每個(gè)線程占用CPU的時(shí)間;搶占式調(diào)度是根據(jù)線程的優(yōu)先級(jí)別來(lái)獲取CPU的使用權(quán)。JVM的線程調(diào)度模式采用了搶占式模式。
所謂的“并發(fā)執(zhí)行”、“同時(shí)”其實(shí)都不是真正意義上的“同時(shí)”。眾所周知,CPU都有個(gè)時(shí)鐘頻率,表示每秒中能執(zhí)行cpu指令的次數(shù)。在每個(gè)時(shí)鐘周期內(nèi),CPU實(shí)際上只能去執(zhí)行一條(也有可能多條)指令。操作系統(tǒng)將進(jìn)程線程進(jìn)行管理,輪流(沒(méi)有固定的順序)分配每個(gè)進(jìn)程很短的一段是時(shí)間(不一定是均分),然后在每個(gè)線程內(nèi)部,程序代碼自己處理該進(jìn)程內(nèi)部線程的時(shí)間分配,多個(gè)線程之間相互的切換去執(zhí)行,這個(gè)切換時(shí)間也是非常短的。因此多任務(wù)、多進(jìn)程、多線程都是操作系統(tǒng)給人的一種宏觀感受,從微觀角度看,程序的運(yùn)行是異步執(zhí)行的。
用一句話做總結(jié):雖然操作系統(tǒng)是多線程的,但CPU每一時(shí)刻只能做一件事,和人的大腦是一樣的,呵呵。
二、Java與多線程
Java語(yǔ)言的多線程需要操作系統(tǒng)的支持。
Java 虛擬機(jī)允許應(yīng)用程序并發(fā)地運(yùn)行多個(gè)執(zhí)行線程。Java語(yǔ)言提供了多線程編程的擴(kuò)展點(diǎn),并給出了功能強(qiáng)大的線程控制API。
在Java中,多線程的實(shí)現(xiàn)有兩種方式: 擴(kuò)展java.lang.Thread類 實(shí)現(xiàn)java.lang.Runnable接口
每個(gè)線程都有一個(gè)優(yōu)先級(jí),高優(yōu)先級(jí)線程的執(zhí)行優(yōu)先于低優(yōu)先級(jí)線程。每個(gè)線程都可以或不可以標(biāo)記為一個(gè)守護(hù)程序。當(dāng)某個(gè)線程中運(yùn)行的代碼創(chuàng)建一個(gè)新 Thread 對(duì)象時(shí),該新線程的初始優(yōu)先級(jí)被設(shè)定為創(chuàng)建線程的優(yōu)先級(jí),并且當(dāng)且僅當(dāng)創(chuàng)建線程是守護(hù)線程時(shí),新線程才是守護(hù)程序。
當(dāng) Java 虛擬機(jī)啟動(dòng)時(shí),通常都會(huì)有單個(gè)非守護(hù)線程(它通常會(huì)調(diào)用某個(gè)指定類的 main 方法)。Java 虛擬機(jī)會(huì)繼續(xù)執(zhí)行線程,直到下列任一情況出現(xiàn)時(shí)為止:
調(diào)用了 Runtime 類的 exit 方法,并且安全管理器允許退出操作發(fā)生。
非守護(hù)線程的所有線程都已停止運(yùn)行,無(wú)論是通過(guò)從對(duì) run 方法的調(diào)用中返回,還是通過(guò)拋出一個(gè)傳播到 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: 通過(guò)擴(kuò)展Thread類實(shí)現(xiàn)多線程 */ public class TestMitiThread { public static void main(String[] rags){ System.out.println(Thread.currentThread().getName()+ “ 線程運(yùn)行開(kāi)始!”);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)行開(kāi)始!”);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)行開(kāi)始!main 線程運(yùn)行結(jié)束!A 線程運(yùn)行開(kāi)始!0 A 1 A B 線程運(yùn)行開(kāi)始!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é)束!說(shuō)明:
程序啟動(dòng)運(yùn)行main時(shí)候,java虛擬機(jī)啟動(dòng)一個(gè)進(jìn)程,主線程main在main()調(diào)用時(shí)候被創(chuàng)建。隨著調(diào)用MitiSay的兩個(gè)對(duì)象的start方法,另外兩個(gè)線程也啟動(dòng)了,這樣,整個(gè)應(yīng)用就在多線程下運(yùn)行。
在一個(gè)方法中調(diào)用Thread.currentThread().getName()方法,可以獲取當(dāng)前線程的名字。在mian方法中調(diào)用該方法,獲取的是主線程的名字。
注意:start()方法的調(diào)用后并不是立即執(zhí)行多線程代碼,而是使得該線程變?yōu)榭蛇\(yùn)行態(tài)(Runnable),什么時(shí)候運(yùn)行是由操作系統(tǒng)決定的。
從程序運(yùn)行的結(jié)果可以發(fā)現(xiàn),多線程程序是亂序執(zhí)行。因此,只有亂序執(zhí)行的代碼才有必要設(shè)計(jì)為多線程。
Thread.sleep()方法調(diào)用目的是不讓當(dāng)前線程獨(dú)自霸占該進(jìn)程所獲取的CPU資源,以留出一定時(shí)間給其他線程執(zhí)行的機(jī)會(huì)。
實(shí)際上所有的多線程代碼執(zhí)行順序都是不確定的,每次執(zhí)行的結(jié)果都是隨機(jī)的。
四、實(shí)現(xiàn)java.lang.Runnable接口
/** * 通過(guò)實(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)行開(kāi)始!”);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)行開(kāi)始!”);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)行開(kāi)始!Thread-0 線程運(yùn)行開(kāi)始!main 線程運(yùn)行結(jié)束!0 Thread-0 Thread-1 線程運(yùn)行開(kāi)始!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é)束!說(shuō)明:
TestMitiThread1類通過(guò)實(shí)現(xiàn)Runnable接口,使得該類有了多線程類的特征。run()方法是多線程程序的一個(gè)約定。所有的多線程代碼都在run方法里面。Thread類實(shí)際上也是實(shí)現(xiàn)了Runnable接口的類。
在啟動(dòng)的多線程的時(shí)候,需要先通過(guò)Thread類的構(gòu)造方法Thread(Runnable target)構(gòu)造出對(duì)象,然后調(diào)用Thread對(duì)象的start()方法來(lái)運(yùn)行多線程代碼。
實(shí)際上所有的多線程代碼都是通過(guò)運(yùn)行Thread的start()方法來(lái)運(yùn)行的。因此,不管是擴(kuò)展Thread類還是實(shí)現(xiàn)Runnable接口來(lái)實(shí)現(xiàn)多線程,最終還是通過(guò)Thread的對(duì)象的API來(lái)控制線程的,熟悉Thread類的API是進(jìn)行多線程編程的基礎(chǔ)。
五、讀解Thread類API
static int MAX_PRIORITY 線程可以具有的最高優(yōu)先級(jí)。static int MIN_PRIORITY 線程可以具有的最低優(yōu)先級(jí)。static int NORM_PRIORITY 分配給線程的默認(rèn)優(yōu)先級(jí)。
構(gòu)造方法摘要
Thread(Runnable target)分配新的 Thread 對(duì)象。Thread(String name)分配新的 Thread 對(duì)象。
方法摘要
static Thread currentThread()返回對(duì)當(dāng)前正在執(zhí)行的線程對(duì)象的引用。ClassLoader getContextClassLoader()返回該線程的上下文 ClassLoader。long getId()返回該線程的標(biāo)識(shí)符。String getName()返回該線程的名稱。int getPriority()返回線程的優(yōu)先級(jí)。Thread.State getState()返回該線程的狀態(tài)。ThreadGroup getThreadGroup()返回該線程所屬的線程組。static boolean holdsLock(Object obj)當(dāng)且僅當(dāng)當(dāng)前線程在指定的對(duì)象上保持監(jiān)視器鎖時(shí),才返回 true。void interrupt()中斷線程。
static boolean interrupted()測(cè)試當(dāng)前線程是否已經(jīng)中斷。boolean isAlive()測(cè)試線程是否處于活動(dòng)狀態(tài)。boolean isDaemon()測(cè)試該線程是否為守護(hù)線程。boolean isInterrupted()測(cè)試線程是否已經(jīng)中斷。void join()等待該線程終止。void join(long millis)等待該線程終止的時(shí)間最長(zhǎng)為 millis 毫秒。void join(long millis, int nanos)等待該線程終止的時(shí)間最長(zhǎng)為 millis 毫秒 + nanos 納秒。void resume()已過(guò)時(shí)。該方法只與 suspend()一起使用,但 suspend()已經(jīng)遭到反對(duì),因?yàn)樗哂兴梨i傾向。有關(guān)更多信息,請(qǐng)參閱為何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對(duì)?。void run()如果該線程是使用獨(dú)立的 Runnable 運(yùn)行對(duì)象構(gòu)造的,則調(diào)用該 Runnable 對(duì)象的 run 方法;否則,該方法不執(zhí)行任何操作并返回。void setContextClassLoader(ClassLoader cl)設(shè)置該線程的上下文 ClassLoader。void setDaemon(boolean on)將該線程標(biāo)記為守護(hù)線程或用戶線程。
static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)設(shè)置當(dāng)線程由于未捕獲到異常而突然終止,并且沒(méi)有為該線程定義其他處理程序時(shí)所調(diào)用的默認(rèn)處理程序。void setName(String name)改變線程名稱,使之與參數(shù) name 相同。void setPriority(int newPriority)更改線程的優(yōu)先級(jí)。
void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)設(shè)置該線程由于未捕獲到異常而突然終止時(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()使該線程開(kāi)始執(zhí)行;Java 虛擬機(jī)調(diào)用該線程的 run 方法。void stop()已過(guò)時(shí)。該方法具有固有的不安全性。用 Thread.stop 來(lái)終止線程將釋放它已經(jīng)鎖定的所有監(jiān)視器(作為沿堆棧向上傳播的未檢查 ThreadDeath 異常的一個(gè)自然后果)。如果以前受這些監(jiān)視器保護(hù)的任何對(duì)象都處于一種不一致的狀態(tài),則損壞的對(duì)象將對(duì)其他線程可見(jiàn),這有可能導(dǎo)致任意的行為。stop 的許多使用都應(yīng)由只修改某些變量以指示目標(biāo)線程應(yīng)該停止運(yùn)行的代碼來(lái)取代。目標(biāo)線程應(yīng)定期檢查該變量,并且如果該變量指示它要停止運(yùn)行,則從其運(yùn)行方法依次返回。如果目標(biāo)線程等待很長(zhǎng)時(shí)間(例如基于一個(gè)條件變量),則應(yīng)使用 interrupt 方法來(lái)中斷該等待。有關(guān)更多信息,請(qǐng)參閱《為何不贊成使用 Thread.stop、Thread.suspend 和 Thread.resume?》。void stop(Throwable obj)已過(guò)時(shí)。該方法具有固有的不安全性。請(qǐng)參閱 stop()以獲得詳細(xì)信息。該方法的附加危險(xiǎn)是它可用于生成目標(biāo)線程未準(zhǔn)備處理的異常(包括若沒(méi)有該方法該線程不太可能拋出的已檢查的異常)。有關(guān)更多信息,請(qǐng)參閱為何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對(duì)?。void suspend()已過(guò)時(shí)。該方法已經(jīng)遭到反對(duì),因?yàn)樗哂泄逃械乃梨i傾向。如果目標(biāo)線程掛起時(shí)在保護(hù)關(guān)鍵系統(tǒng)資源的監(jiān)視器上保持有鎖,則在目標(biāo)線程重新開(kāi)始以前任何線程都不能訪問(wèn)該資源。如果重新開(kāi)始目標(biāo)線程的線程想在調(diào)用 resume 之前鎖定該監(jiān)視器,則會(huì)發(fā)生死鎖。這類死鎖通常會(huì)證明自己是“凍結(jié)”的進(jìn)程。有關(guān)更多信息,請(qǐng)參閱為何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對(duì)?。String toString()返回該線程的字符串表示形式,包括線程名稱、優(yōu)先級(jí)和線程組。static void yield()暫停當(dāng)前正在執(zhí)行的線程對(duì)象,并執(zhí)行其他線程。
六、線程的狀態(tài)轉(zhuǎn)換圖
線程在一定條件下,狀態(tài)會(huì)發(fā)生變化。線程變化的狀態(tài)轉(zhuǎn)換圖如下:
1、新建狀態(tài)(New):新創(chuàng)建了一個(gè)線程對(duì)象。
2、就緒狀態(tài)(Runnable):線程對(duì)象創(chuàng)建后,其他線程調(diào)用了該對(duì)象的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),暫時(shí)停止運(yùn)行。直到線程進(jìn)入就緒狀態(tài),才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)。阻塞的情況分三種:
(一)、等待阻塞:運(yùn)行的線程執(zhí)行wait()方法,JVM會(huì)把該線程放入等待池中。
(二)、同步阻塞:運(yùn)行的線程在獲取對(duì)象的同步鎖時(shí),若該同步鎖被別的線程占用,則JVM會(huì)把該線程放入鎖池中。
(三)、其他阻塞:運(yùn)行的線程執(zhí)行sleep()或join()方法,或者發(fā)出了I/O請(qǐng)求時(shí),JVM會(huì)把該線程置為阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)、或者I/O處理完畢時(shí),線程重新轉(zhuǎn)入就緒狀態(tài)。
5、死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期。
七、線程的調(diào)度
1、調(diào)整線程優(yōu)先級(jí):Java線程有優(yōu)先級(jí),優(yōu)先級(jí)高的線程會(huì)獲得較多的運(yùn)行機(jī)會(huì)。
Java線程的優(yōu)先級(jí)用整數(shù)表示,取值范圍是1~10,Thread類有以下三個(gè)靜態(tài)常量: static int MAX_PRIORITY 線程可以具有的最高優(yōu)先級(jí),取值為10。static int MIN_PRIORITY 線程可以具有的最低優(yōu)先級(jí),取值為1。static int NORM_PRIORITY 分配給線程的默認(rèn)優(yōu)先級(jí),取值為5。
Thread類的setPriority()和getPriority()方法分別用來(lái)設(shè)置和獲取線程的優(yōu)先級(jí)。
每個(gè)線程都有默認(rèn)的優(yōu)先級(jí)。主線程的默認(rèn)優(yōu)先級(jí)為Thread.NORM_PRIORITY。
線程的優(yōu)先級(jí)有繼承關(guān)系,比如A線程中創(chuàng)建了B線程,那么B將和A具有相同的優(yōu)先級(jí)。JVM提供了10個(gè)線程優(yōu)先級(jí),但與常見(jiàn)的操作系統(tǒng)都不能很好的映射。如果希望程序能移植到各個(gè)操作系統(tǒng)中,應(yīng)該僅僅使用Thread類有以下三個(gè)靜態(tài)常量作為優(yōu)先級(jí),這樣能保證同樣的優(yōu)先級(jí)采用了同樣的調(diào)度方式。
2、線程睡眠:Thread.sleep(long millis)方法,使線程轉(zhuǎn)到阻塞狀態(tài)。millis參數(shù)設(shè)定睡眠的時(shí)間,以毫秒為單位。當(dāng)睡眠結(jié)束后,就轉(zhuǎn)為就緒(Runnable)狀態(tài)。sleep()平臺(tái)移植性好。
3、線程等待:Object類中的wait()方法,導(dǎo)致當(dāng)前的線程等待,直到其他線程調(diào)用此對(duì)象的 notify()方法或 notifyAll()喚醒方法。這個(gè)兩個(gè)喚醒方法也是Object類中的方法,行為等價(jià)于調(diào)用 wait(0)一樣。
4、線程讓步:Thread.yield()方法,暫停當(dāng)前正在執(zhí)行的線程對(duì)象,把執(zhí)行機(jī)會(huì)讓給相同或者更高優(yōu)先級(jí)的線程。
5、線程加入:join()方法,等待其他線程終止。在當(dāng)前線程中調(diào)用另一個(gè)線程的join()方法,則當(dāng)前線程轉(zhuǎn)入阻塞狀態(tài),直到另一個(gè)進(jìn)程運(yùn)行結(jié)束,當(dāng)前線程再由阻塞轉(zhuǎn)為就緒狀態(tài)。
6、線程喚醒:Object類中的notify()方法,喚醒在此對(duì)象監(jiān)視器上等待的單個(gè)線程。如果所有線程都在此對(duì)象上等待,則會(huì)選擇喚醒其中一個(gè)線程。選擇是任意性的,并在對(duì)實(shí)現(xiàn)做出決定時(shí)發(fā)生。線程通過(guò)調(diào)用其中一個(gè) wait 方法,在對(duì)象的監(jiān)視器上等待。直到當(dāng)前的線程放棄此對(duì)象上的鎖定,才能繼續(xù)執(zhí)行被喚醒的線程。被喚醒的線程將以常規(guī)方式與在該對(duì)象上主動(dòng)同步的其他所有線程進(jìn)行競(jìng)爭(zhēng);例如,喚醒的線程在作為鎖定此對(duì)象的下一個(gè)線程方面沒(méi)有可靠的特權(quán)或劣勢(shì)。類似的方法還有一個(gè)notifyAll(),喚醒在此對(duì)象監(jiān)視器上等待的所有線程。注意:Thread中suspend()和resume()兩個(gè)方法在JDK1.5中已經(jīng)廢除,不再介紹。因?yàn)橛兴梨i傾向。
7、常見(jiàn)線程名詞解釋
主線程:JVM調(diào)用程序mian()所產(chǎn)生的線程。
當(dāng)前線程:這個(gè)是容易混淆的概念。一般指通過(guò)Thread.currentThread()來(lái)獲取的進(jìn)程。后臺(tái)線程:指為其他線程提供服務(wù)的線程,也稱為守護(hù)線程。JVM的垃圾回收線程就是一個(gè)后臺(tái)線程。
前臺(tái)線程:是指接受后臺(tái)線程服務(wù)的線程,其實(shí)前臺(tái)后臺(tái)線程是聯(lián)系在一起,就像傀儡和幕后操縱者一樣的關(guān)系??苁乔芭_(tái)線程、幕后操縱者是后臺(tái)線程。由前臺(tái)線程創(chuàng)建的線程默認(rèn)也是前臺(tái)線程??梢酝ㄟ^(guò)isDaemon()和setDaemon()方法來(lái)判斷和設(shè)置一個(gè)線程是否為后臺(tái)線程。
本文出自 “熔 巖” 博客,請(qǐng)務(wù)必保留此出處http://lavasoft.blog.51cto.com/62575/27069
第二篇:多線程編程知識(shí)總結(jié)
多線程編程
一、問(wèn)題的提出
1.1問(wèn)題的引出
編寫一個(gè)耗時(shí)的單線程程序:
新建一個(gè)基于對(duì)話框的應(yīng)用程序SingleThread,在主對(duì)話框IDD_SINGLETHREAD_DIALOG添加一個(gè)按鈕,ID為IDC_SLEEP_SIX_SECOND,標(biāo)題為“延時(shí)6秒”,添加按鈕的響應(yīng)函數(shù),代碼如下:
void CSingleThreadDlg::OnSleepSixSecond(){ Sleep(6000);//延時(shí)6秒 } 編譯并運(yùn)行應(yīng)用程序,單擊“延時(shí)6秒”按鈕,你就會(huì)發(fā)現(xiàn)在這6秒期間程序就象“死機(jī)”一樣,不在響應(yīng)其它消息。為了更好地處理這種耗時(shí)的操作,我們有必要學(xué)習(xí)——多線程編程。
1.2多線程概述
進(jìn)程和線程都是操作系統(tǒng)的概念。進(jìn)程是應(yīng)用程序的執(zhí)行實(shí)例,每個(gè)進(jìn)程是由私有的虛擬地址空間、代碼、數(shù)據(jù)和其它各種系統(tǒng)資源組成,進(jìn)程在運(yùn)行過(guò)程中創(chuàng)建的資源隨著進(jìn)程的終止而被銷毀,所使用的系統(tǒng)資源在進(jìn)程終止時(shí)被釋放或關(guān)閉。
線程是進(jìn)程內(nèi)部的一個(gè)執(zhí)行單元。系統(tǒng)創(chuàng)建好進(jìn)程后,實(shí)際上就啟動(dòng)執(zhí)行了該進(jìn)程的主執(zhí)行線程,主執(zhí)行線程以函數(shù)地址形式,比如說(shuō)main或WinMain函數(shù),將程序的啟動(dòng)點(diǎn)提供給Windows系統(tǒng)。主執(zhí)行線程終止了,進(jìn)程也就隨之終止。
每一個(gè)進(jìn)程至少有一個(gè)主執(zhí)行線程,它無(wú)需由用戶去主動(dòng)創(chuàng)建,是由系統(tǒng)自動(dòng)創(chuàng)建的。用戶根據(jù)需要在應(yīng)用程序中創(chuàng)建其它線程,多個(gè)線程并發(fā)地運(yùn)行于同一個(gè)進(jìn)程中。一個(gè)進(jìn)程中的所有線程都在該進(jìn)程的虛擬地址空間中,共同使用這些虛擬地址空間、全局變量和系統(tǒng)資源,所以線程間的通訊非常方便,多線程技術(shù)的應(yīng)用也較為廣泛。
多線程可以實(shí)現(xiàn)并行處理,避免了某項(xiàng)任務(wù)長(zhǎng)時(shí)間占用CPU時(shí)間。要說(shuō)明的一點(diǎn)是,對(duì)于單處理器(CPU)的,為了運(yùn)行所有這些線程,操作系統(tǒng)為每個(gè)獨(dú)立線程安排一些CPU時(shí)間,操作系統(tǒng)以輪換方式向線程提供時(shí)間片,這就給人一種假象,好象這些線程都在同時(shí)運(yùn)行。由此可見(jiàn),如果兩個(gè)非?;钴S的線程為了搶奪對(duì)CPU的控制權(quán),在線程切換時(shí)會(huì)消耗很多的CPU資源,反而會(huì)降低系統(tǒng)的性能。這一點(diǎn)在多線程編程時(shí)應(yīng)該注意。
Win32 SDK函數(shù)支持進(jìn)行多線程的程序設(shè)計(jì),并提供了操作系統(tǒng)原理中的各種同步、互斥和臨界區(qū)等操作。Visual C++中,使用MFC類庫(kù)也實(shí)現(xiàn)了多線程的程序設(shè)計(jì),使得多線程編程更加方便。1.3 Win32 API對(duì)多線程編程的支持
Win32 提供了一系列的API函數(shù)來(lái)完成線程的創(chuàng)建、掛起、恢復(fù)、終結(jié)以及通信等工作。下面將選取其中的一些重要函數(shù)進(jìn)行說(shuō)明。
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)建一個(gè)新的線程,并返回已建線程的句柄,其中各參數(shù)說(shuō)明如下:
lpThreadAttributes:指向一個(gè) SECURITY_ATTRIBUTES 結(jié)構(gòu)的指針,該結(jié)構(gòu)決定了線程的安全屬性,一般置為 NULL;
dwStackSize:指定了線程的堆棧深度,一般都設(shè)置為0;
lpStartAddress:表示新線程開(kāi)始執(zhí)行時(shí)代碼所在函數(shù)的地址,即線程的起始地址。一般情況為(LPTHREAD_START_ROUTINE)ThreadFunc,ThreadFunc 是線程函數(shù)名;
lpParameter:指定了線程執(zhí)行時(shí)傳送給線程的32位參數(shù),即線程函數(shù)的參數(shù);
dwCreationFlags:控制線程創(chuàng)建的附加標(biāo)志,可以取兩種值。如果該參數(shù)為0,線程在被創(chuàng)建后就會(huì)立即開(kāi)始執(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用來(lái)設(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()終止某個(gè)線程的執(zhí)行是不安全的,可能會(huì)引起系統(tǒng)不穩(wěn)定;雖然該函數(shù)立即終止線程的執(zhí)行,但并不釋放線程所占用的資源。因此,一般不建議使用該函數(shù)。
6、BOOL PostThreadMessage(DWORD idThread,UINT Msg,WPARAM wParam,LPARAM lParam);該函數(shù)將一條消息放入到指定線程的消息隊(duì)列中,并且不等到消息被該線程處理時(shí)便返回。idThread:將接收消息的線程的ID;
Msg:指定用來(lái)發(fā)送的消息;
wParam:同消息有關(guān)的字參數(shù);
lParam:同消息有關(guān)的長(zhǎng)參數(shù);
調(diào)用該函數(shù)時(shí),如果即將接收消息的線程沒(méi)有創(chuàng)建消息循環(huán),則該函數(shù)執(zhí)行失敗。
1.4.Win32 API多線程編程例程
例程1 [MultiThread1] 一個(gè)簡(jiǎn)單的線程。注意事項(xiàng):
? Volatile:關(guān)鍵字:
volatile是要求C++編譯器不要自作聰明的把變量緩沖在寄存器里.因?yàn)樵撟兞靠赡軙?huì)被意外的修改。(多個(gè)線程或其他原因)
如從串口讀數(shù)據(jù)的場(chǎng)合,把變量緩沖在寄存器里,下次去讀寄存器就沒(méi)有意義了.因?yàn)榇诘臄?shù)據(jù)可能隨時(shí)會(huì)改變的.加鎖訪問(wèn)用于多個(gè)線程的場(chǎng)合.在進(jìn)入臨界區(qū)時(shí)是肯定要加鎖的.volatile也加上,以保證從內(nèi)存中讀取變量的值.? 終止線程:
Windows終止線程運(yùn)行的四種方法 終止線程運(yùn)行
若要終止線程的運(yùn)行,可以使用下面的方法:
? 線程函數(shù)返回(最好使用這種方法)。
? 通過(guò)調(diào)用 ExitThread 函數(shù),線程將自行撤消(最好不要使用這種方法)。
? 同一個(gè)進(jìn)程或另一個(gè)進(jìn)程中的線程調(diào)用 TerminateThread 函數(shù)(應(yīng)該避免使用這種方法)。
? 包含線程的進(jìn)程終止運(yùn)行(應(yīng)該避免使用這種方法)。
下面將介紹終止線程運(yùn)行的方法,并且說(shuō)明線程終止運(yùn)行時(shí)會(huì)出現(xiàn)什么情況。
? 線程函數(shù)返回
始終都應(yīng)該將線程設(shè)計(jì)成這樣的形式,即當(dāng)想要線程終止運(yùn)行時(shí),它們就能夠返回。這是確保所有線程資源被正確地清除的唯一辦法。
如果線程能夠返回,就可以確保下列事項(xiàng)的實(shí)現(xiàn):
? 在線程函數(shù)中創(chuàng)建的所有 C++ 對(duì)象均將通過(guò)它們的撤消函數(shù)正確地撤消。
? 操作系統(tǒng)將正確地釋放線程堆棧使用的內(nèi)存。
? 系統(tǒng)將線程的退出代碼(在線程的內(nèi)核對(duì)象中維護(hù))設(shè)置為線程函數(shù)的返回值。
? 系統(tǒng)將遞減線程內(nèi)核對(duì)象的使用計(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++ 類對(duì)象)將不被撤消。由于這個(gè)原因,最好從線程函數(shù)返回,而不是通過(guò)調(diào)用 ExitThread 來(lái)返回。
當(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)識(shí)被終止運(yùn)行的線程的句柄。當(dāng)線程終止運(yùn)行時(shí),它的退出代碼成為你作為 dwExitCode 參數(shù)傳遞的值。同時(shí),線程的內(nèi)核對(duì)象的使用計(jì)數(shù)也被遞減。
注意 TerminateThread 函數(shù)是異步運(yùn)行的函數(shù),也就是說(shuō),它告訴系統(tǒng)你想要線程終止運(yùn)行,但是,當(dāng)函數(shù)返回時(shí),不能保證線程被撤消。如果需要確切地知道該線程已經(jīng)終止運(yùn)行,必須調(diào)用 WaitForSingleObject 或者類似的函數(shù),傳遞線程的句柄。
設(shè)計(jì)良好的應(yīng)用程序從來(lái)不使用這個(gè)函數(shù),因?yàn)楸唤K止運(yùn)行的線程收不到它被撤消的通知。線程不能正確地清除,并且不能防止自己被撤消。
注意 當(dāng)使用返回或調(diào)用 ExitThread 的方法撤消線程時(shí),該線程的內(nèi)存堆棧也被撤消。但是,如果使用 TerminateThread,那么在擁有線程的進(jìn)程終止運(yùn)行之前,系統(tǒng)不撤消該線程的堆棧。Microsoft故意用這種方法來(lái)實(shí)現(xiàn) TerminateThread。如果其他仍然正在執(zhí)行的線程要引用強(qiáng)制撤消的線程堆棧上的值,那么其他的線程就會(huì)出現(xiàn)訪問(wèn)違規(guī)的問(wèn)題。如果將已經(jīng)撤消的線程的堆棧留在內(nèi)存中,那么其他線程就可以繼續(xù)很好地運(yùn)行。
此外,當(dāng)線程終止運(yùn)行時(shí),DLL 通常接收通知。如果使用 TerminateThread 強(qiáng)迫線程終止,DLL 就不接收通知,這能阻止適當(dāng)?shù)那宄?,在進(jìn)程終止運(yùn)行時(shí)撤消線程。當(dāng)線程終止運(yùn)行時(shí),會(huì)發(fā)生下列操作:
? 線程擁有的所有用戶對(duì)象均被釋放。在 Windows 中,大多數(shù)對(duì)象是由包含創(chuàng)建這些對(duì)象的線程的進(jìn)程擁有的。但是一個(gè)線程擁有兩個(gè)用戶對(duì)象,即窗口和掛鉤。當(dāng)線程終止運(yùn)行時(shí),系統(tǒng)會(huì)自動(dòng)撤消任何窗口,并且卸載線程創(chuàng)建的或安裝的任何掛鉤。其他對(duì)象只有在擁有線程的進(jìn)程終止運(yùn)行時(shí)才被撤消。
? 線程的退出代碼從 STILL_ACTIVE 改為傳遞給 ExitThread 或 TerminateThread 的代碼。
? 線程內(nèi)核對(duì)象的狀態(tài)變?yōu)橐淹ㄖ?/p>
? 如果線程是進(jìn)程中最后一個(gè)活動(dòng)線程,系統(tǒng)也將進(jìn)程視為已經(jīng)終止運(yùn)行。
? 線程內(nèi)核對(duì)象的使用計(jì)數(shù)遞減 1。
當(dāng)一個(gè)線程終止運(yùn)行時(shí),在與它相關(guān)聯(lián)的線程內(nèi)核對(duì)象的所有未結(jié)束的引用關(guān)閉之前,該內(nèi)核對(duì)象不會(huì)自動(dòng)被釋放。
一旦線程不再運(yùn)行,系統(tǒng)中就沒(méi)有別的線程能夠處理該線程的句柄。然而別的線程可以調(diào)用 GetExitcodeThread 來(lái)檢查由 hThread 標(biāo)識(shí)的線程是否已經(jīng)終止運(yùn)行。如果它已經(jīng)終止運(yùn)行,則確定它的退出代碼:
BOOL GetExitCodeThread(HANDLE hThread, PDOWRD pdwExitCode);退出代碼的值在 pdwExitCode 指向的 DWORD 中返回。如果調(diào)用 GetExitCodeThread 時(shí)線程尚未終止運(yùn)行,該函數(shù)就用 STILL_ACTIVE 標(biāo)識(shí)符(定義為 0x103)填入 DWORD。如果該函數(shù)運(yùn)行成功,便返回 TRUE。
? 線程的定義:
例程2[MultiThread2] 傳送一個(gè)一個(gè)整型的參數(shù)到一個(gè)線程中,以及如何等待一個(gè)線程完成處理。
DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);
hHandle:為要監(jiān)視的對(duì)象(一般為同步對(duì)象,也可以是線程)的句柄;
dwMilliseconds:為hHandle對(duì)象所設(shè)置的超時(shí)值,單位為毫秒;
當(dāng)在某一線程中調(diào)用該函數(shù)時(shí),線程暫時(shí)掛起,系統(tǒng)監(jiān)視hHandle所指向的對(duì)象的狀態(tài)。如果在掛起的dwMilliseconds毫秒內(nèi),線程所等待的對(duì)象變?yōu)橛行盘?hào)狀態(tài),則該函數(shù)立即返回;如果超時(shí)時(shí)間已經(jīng)到達(dá)dwMilliseconds毫秒,但hHandle所指向的對(duì)象還沒(méi)有變成有信號(hào)狀態(tài),函數(shù)照樣返回。參數(shù)dwMilliseconds有兩個(gè)具有特殊意義的值:0和INFINITE。若為0,則該函數(shù)立即返回;若為INFINITE,則線程一直被掛起,直到hHandle所指向的對(duì)象變?yōu)橛行盘?hào)狀態(tài)時(shí)為止。
例程3[MultiThread3] 傳送一個(gè)結(jié)構(gòu)體給一個(gè)線程函數(shù),可以通過(guò)傳送一個(gè)指向結(jié)構(gòu)體的指針參數(shù)來(lái)完成。補(bǔ)充一點(diǎn):如果你在void CMultiThread3Dlg::OnStart()函數(shù)中添加/* */語(yǔ)句,編譯運(yùn)行你就會(huì)發(fā)現(xiàn)進(jìn)度條不進(jìn)行刷新,主線程也停止了反應(yīng)。什么原因呢?這是因?yàn)閃aitForSingleObject函數(shù)等待子線程(ThreadFunc)結(jié)束時(shí),導(dǎo)致了線程死鎖。因?yàn)閃aitForSingleObject函數(shù)會(huì)將主線程掛起(任何消息都得不到處理),而子線程ThreadFunc正在設(shè)置進(jìn)度條,一直在等待主線程將刷新消息處理完畢返回才會(huì)檢測(cè)通知事件。這樣兩個(gè)線程都在互相等待,死鎖發(fā)生了,編程時(shí)應(yīng)注意避免。
例程4[MultiThread4] 測(cè)試在Windows下最多可創(chuàng)建線程的數(shù)目。
二、MFC中的多線程開(kāi)發(fā)
2.1 MFC對(duì)多線程編程的支持
MFC中有兩類線程,分別稱之為工作者線程和用戶界面線程。二者的主要區(qū)別在于工作者線程沒(méi)有消息循環(huán),而用戶界面線程有自己的消息隊(duì)列和消息循環(huán)。
工作者線程沒(méi)有消息機(jī)制,通常用來(lái)執(zhí)行后臺(tái)計(jì)算和維護(hù)任務(wù),如冗長(zhǎng)的計(jì)算過(guò)程,打印機(jī)的后臺(tái)打印等。用戶界面線程一般用于處理獨(dú)立于其他線程執(zhí)行之外的用戶輸入,響應(yīng)用戶及系統(tǒng)所產(chǎn)生的事件和消息等。但對(duì)于Win32的API編程而言,這兩種線程是沒(méi)有區(qū)別的,它們都只需線程的啟動(dòng)地址即可啟動(dòng)線程來(lái)執(zhí)行任務(wù)。
在MFC中,一般用全局函數(shù)AfxBeginThread()來(lái)創(chuàng)建并初始化一個(gè)線程的運(yùn)行,該函數(shù)有兩種重載形式,分別用于創(chuàng)建工作者線程和用戶界面線程。兩種重載函數(shù)原型和參數(shù)分別說(shuō)明如下:
(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);請(qǐng)注意,ExecutingFunction()應(yīng)返回一個(gè)UINT類型的值,用以指明該函數(shù)結(jié)束的原因。一般情況下,返回0表明執(zhí)行成功。
pParam:傳遞給線程函數(shù)的一個(gè)32位參數(shù),執(zhí)行函數(shù)將用某種方式解釋該值。它可以是數(shù)值,或是指向一個(gè)結(jié)構(gòu)的指針,甚至可以被忽略;
nPriority:線程的優(yōu)先級(jí)。如果為0,則線程與其父線程具有相同的優(yōu)先級(jí);
nStackSize:線程為自己分配堆棧的大小,其單位為字節(jié)。如果nStackSize被設(shè)為0,則線程的堆棧被設(shè)置成與父線程堆棧相同大??; dwCreateFlags:如果為0,則線程在創(chuàng)建后立刻開(kāi)始執(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 的一個(gè)導(dǎo)出類的運(yùn)行時(shí)類對(duì)象的指針,該導(dǎo)出類定義了被創(chuàng)建的用戶界面線程的啟動(dòng)、退出等;其它參數(shù)的意義同形式1。使用函數(shù)的這個(gè)原型生成的線程也有消息機(jī)制,在以后的例子中我們將發(fā)現(xiàn)同主線程的機(jī)制幾乎一樣。下面對(duì)CWinThread類的數(shù)據(jù)成員及常用函數(shù)進(jìn)行簡(jiǎn)要說(shuō)明。
? ? ? 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值用來(lái)標(biāo)識(shí)各種錯(cuò)誤。同InitInstance()成員函數(shù)一樣,該函數(shù)也只適用于用戶界面線程。
2.2 MFC多線程編程實(shí)例
例程5 MultiThread5 為了與Win32 API對(duì)照,使用MFC 類庫(kù)編程實(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(){
}
請(qǐng)注意以下兩點(diǎn):
A、在UIThreadDlg.cpp的開(kāi)頭加入語(yǔ)句: #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)行一個(gè)標(biāo)準(zhǔn)的消息循環(huán),并且當(dāng)收到WM_QUIT消息后中斷,在消息循環(huán)過(guò)程中,Run()函數(shù)檢測(cè)到線程空閑時(shí)(沒(méi)有消息),也將調(diào)用OnIdle()函數(shù),最后Run()函數(shù)返回,MFC調(diào)用ExitInstance()函數(shù)清理資源。
你可以創(chuàng)建一個(gè)沒(méi)有界面而有消息循環(huán)的線程,例如:你可以從CWinThread派生一個(gè)新類,在InitInstance函數(shù)中完成某項(xiàng)任務(wù)并返回FALSE,這表示僅執(zhí)行InitInstance函數(shù)中的任務(wù)而不執(zhí)行消息循環(huán),你可以通過(guò)這種方法,完成一個(gè)工作者線程的功能。
三、線程間通訊
3.1通訊方式
一般而言,應(yīng)用程序中的一個(gè)次要線程總是為主線程執(zhí)行特定的任務(wù),這樣,主線程和次要線程間必定有一個(gè)信息傳遞的渠道,也就是主線程和次要線程間要進(jìn)行通信。這種線程間的通信不但是難以避免的,而且在多線程編程中也是復(fù)雜和頻繁的,下面將進(jìn)行說(shuō)明。
3.1.1使用全局變量進(jìn)行通信
由于屬于同一個(gè)進(jìn)程的各個(gè)線程共享操作系統(tǒng)分配該進(jìn)程的資源,故解決線程間通信最簡(jiǎn)單的一種方法是使用全局變量。對(duì)于標(biāo)準(zhǔn)類型的全局變量,建議使用volatile 修飾符,它告訴編譯器無(wú)需對(duì)該變量作任何的優(yōu)化,即無(wú)需將它放到一個(gè)寄存器中,并且該值可被外部改變。如果線程間所需傳遞的信息較復(fù)雜,可以定義一個(gè)結(jié)構(gòu),通過(guò)傳遞指向該結(jié)構(gòu)的指針進(jìn)行傳遞信息。
3.1.2使用自定義消息
可以在一個(gè)線程的執(zhí)行函數(shù)中向另一個(gè)線程發(fā)送自定義的消息來(lái)達(dá)到通信的目的。一個(gè)線程向另外一個(gè)線程發(fā)送消息是通過(guò)操作系統(tǒng)實(shí)現(xiàn)的。利用Windows操作系統(tǒng)的消息驅(qū)動(dòng)機(jī)制,當(dāng)一個(gè)線程發(fā)出一條消息時(shí),操作系統(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基本概念
雖然多線程能給我們帶來(lái)好處,但是也有不少問(wèn)題需要解決。例如,對(duì)于像磁盤驅(qū)動(dòng)器這樣獨(dú)占性系統(tǒng)資源,由于線程可以執(zhí)行進(jìn)程的任何代碼段,且線程的運(yùn)行是由系統(tǒng)調(diào)度自動(dòng)完成的,具有一定的不確定性,因此就有可能出現(xiàn)兩個(gè)線程同時(shí)對(duì)磁盤驅(qū)動(dòng)器進(jìn)行操作,從而出現(xiàn)操作錯(cuò)誤;又例如,對(duì)于銀行系統(tǒng)的計(jì)算機(jī)來(lái)說(shuō),可能使用一個(gè)線程來(lái)更新其用戶數(shù)據(jù)庫(kù),而用另外一個(gè)線程來(lái)讀取數(shù)據(jù)庫(kù)以響應(yīng)儲(chǔ)戶的需要,極有可能讀數(shù)據(jù)庫(kù)的線程讀取的是未完全更新的數(shù)據(jù)庫(kù),因?yàn)榭赡茉谧x的時(shí)候只有一部分?jǐn)?shù)據(jù)被更新過(guò)。
使隸屬于同一進(jìn)程的各線程協(xié)調(diào)一致地工作稱為線程的同步。MFC提供了多種同步對(duì)象,下面只介紹最常用的四種:
臨界區(qū)(CCriticalSection)
事件(CEvent)
互斥量(CMutex)
信號(hào)量(CSemaphore)
通過(guò)這些類,可以比較容易地做到線程同步。
4.2使用 CCriticalSection 類
當(dāng)多個(gè)線程訪問(wèn)一個(gè)獨(dú)占性共享資源時(shí),可以使用“臨界區(qū)”對(duì)象。任一時(shí)刻只有一個(gè)線程可以擁有臨界區(qū)對(duì)象,擁有臨界區(qū)的線程可以訪問(wèn)被保護(hù)起來(lái)的資源或代碼段,其他希望進(jìn)入臨界區(qū)的線程將被掛起等待,直到擁有臨界區(qū)的線程放棄臨界區(qū)時(shí)為止,這樣就保證了不會(huì)在同一時(shí)刻出現(xiàn)多個(gè)線程訪問(wèn)共享資源。
CCriticalSection類的用法非常簡(jiǎn)單,步驟如下:
1.定義CCriticalSection類的一個(gè)全局對(duì)象(以使各個(gè)線程均能訪問(wèn)),如CCriticalSection critical_section;
2.在訪問(wèn)需要保護(hù)的資源或代碼之前,調(diào)用CCriticalSection類的成員Lock()獲得臨界區(qū)對(duì)象: critical_section.Lock();3.在線程中調(diào)用該函數(shù)來(lái)使線程獲得它所請(qǐng)求的臨界區(qū)。如果此時(shí)沒(méi)有其它線程占有臨界區(qū)對(duì)象,則調(diào)用Lock()的線程獲得臨界區(qū);否則,線程將被掛起,并放入到一個(gè)系統(tǒng)隊(duì)列中等待,直到當(dāng)前擁有臨界區(qū)的線程釋放了臨界區(qū)時(shí)為止。
4.訪問(wèn)臨界區(qū)完畢后,使用CCriticalSection的成員函數(shù)Unlock()來(lái)釋放臨界區(qū):critical_section.Unlock();通俗講,就是線程A執(zhí)行到critical_section.Lock();語(yǔ)句時(shí),如果其它線程(B)正在執(zhí)行critical_section.Lock();語(yǔ)句后且critical_section.Unlock();語(yǔ)句前的語(yǔ)句時(shí),線程A就會(huì)等待,直到線程B執(zhí)行完critical_section.Unlock();語(yǔ)句,線程A才會(huì)繼續(xù)執(zhí)行。
例程8 MultiThread8 4.3使用 CEvent 類
CEvent 類提供了對(duì)事件的支持。事件是一個(gè)允許一個(gè)線程在某種情況發(fā)生時(shí),喚醒另外一個(gè)線程的同步對(duì)象。例如在某些網(wǎng)絡(luò)應(yīng)用程序中,一個(gè)線程(記為A)負(fù)責(zé)監(jiān)聽(tīng)通訊端口,另外一個(gè)線程(記為B)負(fù)責(zé)更新用戶數(shù)據(jù)。通過(guò)使用CEvent 類,線程A可以通知線程B何時(shí)更新用戶數(shù)據(jù)。每一個(gè)CEvent 對(duì)象可以有兩種狀態(tài):有信號(hào)狀態(tài)和無(wú)信號(hào)狀態(tài)。線程監(jiān)視位于其中的CEvent 類對(duì)象的狀態(tài),并在相應(yīng)的時(shí)候采取相應(yīng)的操作。
在MFC中,CEvent 類對(duì)象有兩種類型:人工事件和自動(dòng)事件。一個(gè)自動(dòng)CEvent 對(duì)象在被至少一個(gè)線程釋放后會(huì)自動(dòng)返回到無(wú)信號(hào)狀態(tài);而人工事件對(duì)象獲得信號(hào)后,釋放可利用線程,但直到調(diào)用成員函數(shù)ReSetEvent()才將其設(shè)置為無(wú)信號(hào)狀態(tài)。在創(chuàng)建CEvent 類的對(duì)象時(shí),默認(rèn)創(chuàng)建的是自動(dòng)事件。CEvent 類的各成員函數(shù)的原型和參數(shù)說(shuō)明如下:
1、CEvent(BOOL bInitiallyOwn=FALSE,BOOL bManualReset=FALSE,LPCTSTR lpszName=NULL,LPSECURITY_ATTRIBUTES lpsaAttribute=NULL);bInitiallyOwn:指定事件對(duì)象初始化狀態(tài),TRUE為有信號(hào),F(xiàn)ALSE為無(wú)信號(hào);
bManualReset:指定要?jiǎng)?chuàng)建的事件是屬于人工事件還是自動(dòng)事件。TRUE為人工事件,F(xiàn)ALSE為自動(dòng)事件;
后兩個(gè)參數(shù)一般設(shè)為NULL,在此不作過(guò)多說(shuō)明。
2、BOOL CEvent::SetEvent();
將 CEvent 類對(duì)象的狀態(tài)設(shè)置為有信號(hào)狀態(tài)。如果事件是人工事件,則 CEvent 類對(duì)象保持為有信號(hào)狀態(tài),直到調(diào)用成員函數(shù)ResetEvent()將 其重新設(shè)為無(wú)信號(hào)狀態(tài)時(shí)為止。如果CEvent 類對(duì)象為自動(dòng)事件,則在SetEvent()將事件設(shè)置為有信號(hào)狀態(tài)后,CEvent 類對(duì)象由系統(tǒng)自動(dòng)重置為無(wú)信號(hào)狀態(tài)。
如果該函數(shù)執(zhí)行成功,則返回非零值,否則返回零。
3、BOOL CEvent::ResetEvent();
該函數(shù)將事件的狀態(tài)設(shè)置為無(wú)信號(hào)狀態(tài),并保持該狀態(tài)直至SetEvent()被調(diào)用時(shí)為止。由于自動(dòng)事件是由系統(tǒng)自動(dòng)重置,故自動(dòng)事件不需要調(diào)用該函數(shù)。如果該函數(shù)執(zhí)行成功,返回非零值,否則返回零。一般通過(guò)調(diào)用WaitForSingleObject函數(shù)來(lái)監(jiān)視事件狀態(tài)。前面已經(jīng)介紹了該函數(shù)。由于語(yǔ)言描述的原因,CEvent 類的理解確實(shí)有些難度,只要通過(guò)下面例程,多看幾遍就可理解。例程9 MultiThread9 仔細(xì)分析這兩個(gè)線程函數(shù), 就會(huì)正確理解CEvent 類。線程WriteD執(zhí)行到 WaitForSingleObject(eventWriteD.m_hObject,INFINITE);處等待,直到事件eventWriteD為有信號(hào)該線程才往下執(zhí)行,因?yàn)閑ventWriteD對(duì)象是自動(dòng)事件,則當(dāng)WaitForSingleObject()返回時(shí),系統(tǒng)自動(dòng)把eventWriteD對(duì)象重置為無(wú)信號(hào)狀態(tài)。
4.4使用CMutex 類
互斥對(duì)象與臨界區(qū)對(duì)象很像.互斥對(duì)象與臨界區(qū)對(duì)象的不同在于:互斥對(duì)象可以在進(jìn)程間使用,而臨界區(qū)對(duì)象只能在同一進(jìn)程的各線程間使用。當(dāng)然,互斥對(duì)象也可以用于同一進(jìn)程的各個(gè)線程間,但是在這種情況下,使用臨界區(qū)會(huì)更節(jié)省系統(tǒng)資源,更有效率。
4.5使用CSemaphore 類
當(dāng)需要一個(gè)計(jì)數(shù)器來(lái)限制可以使用某個(gè)線程的數(shù)目時(shí),可以使用“信號(hào)量”對(duì)象。CSemaphore 類的對(duì)象保存了對(duì)當(dāng)前訪問(wèn)某一指定資源的線程的計(jì)數(shù)值,該計(jì)數(shù)值是當(dāng)前還可以使用該資源的線程的數(shù)目。如果這個(gè)計(jì)數(shù)達(dá)到了零,則所有對(duì)這個(gè)CSemaphore 類對(duì)象所控制的資源的訪問(wèn)嘗試都被放入到一個(gè)隊(duì)列中等待,直到超時(shí)或計(jì)數(shù)值不為零時(shí)為止。一個(gè)線程被釋放已訪問(wèn)了被保護(hù)的資源時(shí),計(jì)數(shù)值減1;一個(gè)線程完成了對(duì)被控共享資源的訪問(wèn)時(shí),計(jì)數(shù)值增1。這個(gè)被CSemaphore 類對(duì)象所控制的資源可以同時(shí)接受訪問(wèn)的最大線程數(shù)在該對(duì)象的構(gòu)建函數(shù)中指定。
CSemaphore 類的構(gòu)造函數(shù)原型及參數(shù)說(shuō)明如下:
CSemaphore(LONG lInitialCount=1,LONG lMaxCount=1,LPCTSTR pstrName=NULL,LPSECURITY_ATTRIBUTES lpsaAttributes=NULL);lInitialCount:信號(hào)量對(duì)象的初始計(jì)數(shù)值,即可訪問(wèn)線程數(shù)目的初始值;
lMaxCount:信號(hào)量對(duì)象計(jì)數(shù)值的最大值,該參數(shù)決定了同一時(shí)刻可訪問(wèn)由信號(hào)量保護(hù)的資源的線程最大數(shù)目;
后兩個(gè)參數(shù)在同一進(jìn)程中使用一般為NULL,不作過(guò)多討論;
在用CSemaphore 類的構(gòu)造函數(shù)創(chuàng)建信號(hào)量對(duì)象時(shí)要同時(shí)指出允許的最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)。一般是將當(dāng)前可用資源計(jì)數(shù)設(shè)置為最大資源計(jì)數(shù),每增加一個(gè)線程對(duì)共享資源的訪問(wèn),當(dāng)前可用資源計(jì)數(shù)就會(huì)減1,只要當(dāng)前可用資源計(jì)數(shù)是大于0的,就可以發(fā)出信號(hào)量信號(hào)。但是當(dāng)前可用計(jì)數(shù)減小到0時(shí),則說(shuō)明當(dāng)前占用資源的線程數(shù)已經(jīng)達(dá)到了所允許的最大數(shù)目,不能再允許其它線程的進(jìn)入,此時(shí)的信號(hào)量信號(hào)將無(wú)法發(fā)出。線程在處理完共享資源后,應(yīng)在離開(kāi)的同時(shí)通過(guò)ReleaseSemaphore()函數(shù)將當(dāng)前可用資源數(shù)加1。例程10 MultiThread10 為了文件中能夠正確使用同步類,在文件開(kāi)頭添加: #include “afxmt.h” 定義信號(hào)量對(duì)象和一個(gè)字符數(shù)組,為了能夠在不同線程間使用,定義為全局變量:CSemaphore semaphoreWrite(2,2);//資源最多訪問(wèn)線程2個(gè),當(dāng)前可訪問(wèn)線程數(shù)2個(gè)
在信號(hào)量對(duì)象有信號(hào)的狀態(tài)下,線程執(zhí)行到WaitForSingleObject語(yǔ)句處繼續(xù)執(zhí)行,同時(shí)可用線程數(shù)減1;若線程執(zhí)行到WaitForSingleObject語(yǔ)句時(shí)信號(hào)量對(duì)象無(wú)信號(hào),線程就在這里等待,直到信號(hào)量對(duì)象有信號(hào)線程才往下執(zhí)行。
第三篇:【java總結(jié)】多線程(基礎(chǔ)篇)
【java總結(jié)】多線程(基礎(chǔ)篇)
Java的線程分為5種狀態(tài):創(chuàng)建、就緒、運(yùn)行、阻塞和死亡。
創(chuàng)建:
在java種創(chuàng)建線程的方式有兩種,一種是通過(guò)繼承Thread類并且重寫run方法,run方法中執(zhí)行的代碼便是線程執(zhí)行的代碼。另一種是通過(guò)實(shí)現(xiàn)Runnable接口,并將該接口實(shí)例傳入一個(gè)Thread實(shí)例。通過(guò)對(duì)Thread的引用調(diào)用start()方法,即可讓線程進(jìn)入就緒狀態(tài)。如果直接調(diào)用run方法,并不會(huì)生成線程,而是在當(dāng)前線程中把run()當(dāng)做一個(gè)普通方法執(zhí)行。[java] view plain copy public class Thread1 extends Thread{
/*
* 實(shí)現(xiàn)線程的方法一:通過(guò)繼承Thread并覆蓋run()方法來(lái)實(shí)現(xiàn)多線程。
*/
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+“線程開(kāi)始!”);
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+“ ”+i);
try{
sleep((int)Math.random()*10);
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+“線程結(jié)束!”);
}
}
[java] view plain copy public class Thread2 implements Runnable{
/*
* 實(shí)現(xiàn)線程的方法二:通過(guò)實(shí)現(xiàn)Runnable接口來(lái)實(shí)現(xiàn)多線程
* 實(shí)現(xiàn)Runnable接口比繼承Thread類所具有的優(yōu)勢(shì):
* 1):適合多個(gè)相同的程序代碼的線程去處理同一個(gè)資源
* 2):可以避免java中的單繼承的限制
* 3):增加程序的健壯性,代碼可以被多個(gè)線程共享,代碼和數(shù)據(jù)獨(dú)立
*/
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+“線程開(kāi)始!”);
for(i004km.cnnt i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+“ ”+i);
try{
Thread.sleep((int)Math.random()*10);
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+“線程結(jié)束!”);
}
}
就緒:
處于就緒狀態(tài)的線程隨時(shí)可以被JVM的線程調(diào)度器調(diào)度,進(jìn)入運(yùn)行狀態(tài)。對(duì)于處于就緒狀態(tài)的線程,我們并不能對(duì)他們被調(diào)度的順序進(jìn)行任何估計(jì),也就是說(shuō),線程的執(zhí)行順序是不可預(yù)測(cè)的。處于運(yùn)行狀態(tài)的線程,通過(guò)調(diào)用yield()方法,可以返回到就緒狀態(tài),然而它有可能瞬間被再次調(diào)度。yield()方法把運(yùn)行機(jī)會(huì)讓給了同等優(yōu)先級(jí)的其他線程。
[java] view plain copy public class ThreadYield extends Thread{
@Override
public void run(){
for(int i = 1;i <= 50;i++){
System.out.println(“" +Thread.currentThread().getName()+ ”-----“ + i);
// 當(dāng)i==25時(shí),該線程就會(huì)把CPU時(shí)間讓掉,讓其他或者自己的線程執(zhí)行(也就是誰(shuí)先搶到誰(shuí)執(zhí)行)
if(i==25){
this.yield();
}
}
}
}
[java] view plain copy public class ThreadYieldTest {
/*
* Thread.yield():暫停當(dāng)前正在執(zhí)行的線程對(duì)象,并執(zhí)行其他線程。
* 該方法讓當(dāng)前線程回到可運(yùn)行狀態(tài),以允許其他具有相同優(yōu)先級(jí)的線程獲得運(yùn)行機(jī)會(huì)。
* 但是實(shí)際中無(wú)法保證yield()達(dá)到讓步目的,因?yàn)楫?dāng)前線程有可能被線程調(diào)度程序再次選中。
*/
public static void main(String[] args){
ThreadYield thread1=new ThreadYield();
ThreadYield thread2=new ThreadYield();
thread1.start();
thread2.start();
}
}
運(yùn)行:
處于運(yùn)行狀態(tài)的線程隨時(shí)有可能被線程調(diào)度器換下,進(jìn)入到就緒狀態(tài)。想要規(guī)定線程的順序,需要調(diào)用join方法,對(duì)某個(gè)線程 的調(diào)用join方法,則主線程會(huì)阻塞到該線程執(zhí)行完后再繼續(xù)執(zhí)行。或者使用一種叫做鎖的機(jī)制(下文會(huì)提及)。當(dāng)一個(gè)線程完成它run()里面的所有工作時(shí),線程會(huì)自動(dòng)死亡。調(diào)用sleep(),線程會(huì)進(jìn)入休眠,并且在一段時(shí)間內(nèi)不會(huì)被再度調(diào)用。睡眠時(shí)間過(guò)后,線程才再次進(jìn)入就緒隊(duì)列中。
[java] view plain copy public class ThreadJoinTest {
/*
* join是Thread類的一個(gè)方法,作用是等待該線程終止。例如對(duì)子線程A調(diào)用join()方法,* 主線程將等待子線程A終止后才能繼續(xù)后面的代碼。
*/
public static void main(String[] args){
System.out.println(”主線程開(kāi)始!“);
Thread1 thread1=new Thread1();
Thread1 thread2=new Thread1();
thread1.start();
thread2.start();
try{
thread1.join();
}catch(InterruptedException e){
e.printStackTrace();
}
try{
thread2.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(”主線程結(jié)束!“);
} }
死亡:
線程因?yàn)榇a執(zhí)行完畢而正常結(jié)束自身線程,或者因?yàn)槟承┊惓6Y(jié)束線程。
[java] view plain copy public class ThreadInterrupt extends Thread{
/*
* wait()和sleep()都可以通過(guò)interrupt()方法 打斷線程的暫停狀態(tài),從而使線程立刻拋出Interrupted004km.cnt i=0;i<3;i++){
new Thread(new Runnable(){
@Override
public void run(){
ThreadSynchronizedTest x=new ThreadSynchronizedTest();
System.out.println(”value=“+x.getNext());
System.out.println(”value=“+x.getNext2());
System.out.println(”value=“+x.getNext3());
}
}).start();
}
}
}
阻塞:
阻塞跟Obj.wait(),Obj.notify()方法有關(guān)。當(dāng)調(diào)用wait方法時(shí),線程釋放對(duì)象鎖,進(jìn)入阻塞狀態(tài),直到其他線程喚醒它。
Obj.wait(),與Obj.notify()必須要與synchronized(Obj)一起使用
Obj.notify()作用:對(duì)對(duì)象鎖的喚醒操作。notify()調(diào)用后,并不是馬上就釋放對(duì)象鎖的,而 是在相應(yīng)的synchronized(){}語(yǔ)句塊執(zhí)行結(jié)束,自動(dòng)釋放鎖后,JVM會(huì)在wait()對(duì)象鎖的線 程中隨機(jī)選取一線程,賦予其對(duì)象鎖,喚醒線程,繼續(xù)執(zhí)行。
Obj.wait()作用:線程在獲取對(duì)象鎖后,主動(dòng)釋放對(duì)象鎖,同時(shí)本線程休眠,直到有其它線程調(diào)用
對(duì)象的notify()喚醒該線程,才能繼續(xù)獲取對(duì)象鎖,并繼續(xù)執(zhí)行。下面我們通過(guò)一道題目來(lái)加深理解。
問(wèn)題:建立三個(gè)線程,A線程打印10次A,B線程打印10次B,C線程打印10次C,要求線程同時(shí)運(yùn)行,交替打印10次ABC。代碼如下:
[java] vie004km.cnw plain copy public class ThreadPrintABCTest {
/*
* 建立三個(gè)線程,A線程打印10次A,B線程打印10次B,C線程打印10次C,要求線程同時(shí)運(yùn)行,交替打印10次ABC。
*
*/
public static void main(String[] args)throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
ThreadPrintABC pa = new ThreadPrintABC(”A“, c, a);
ThreadPrintABC pb = new ThreadPrintABC(”B“, a, b);
ThreadPrintABC pc = new ThreadPrintABC(”C“, b, c);
new Thread(pa).start();
Thread.sleep(100);//確保按順序A、B、C執(zhí)行
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
Thread.sleep(100);
}
}
[java] view plain copy public class ThreadPrintABC implements Runnable{
private String data;
private Object pre;
private Object self;
public ThreadPrintABC(String data,Object pre,Object self){
this.data=data;
this.pre=pre;
this.self=self;
}
@Override
public void run(){
int count=10;
while(count>0){
synchronized(pre){
synchronized(self){
if(data==”C"){
System.out.println(data);
}else{
System.out.print(data);
}
count--;
self.notify();
}
try{
pre.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
}
死鎖:
兩個(gè)或兩個(gè)以上的線程在執(zhí)行過(guò)程當(dāng)中,由于競(jìng)爭(zhēng)資源或者彼此之間通信而造成的一種阻塞現(xiàn)象。比如,當(dāng)線程A調(diào)用wait()方法等待線程B的喚醒,而線程B同時(shí)也調(diào)用wait方法等待線程A的喚醒,這時(shí)兩個(gè)線程將陷入僵持狀態(tài),永遠(yuǎn)處在阻塞狀態(tài),成為死鎖進(jìn)程,即兩個(gè)線程永遠(yuǎn)也不會(huì)被執(zhí)行。
sleep方法與wait方法的區(qū)別及細(xì)節(jié):
sleep()睡眠時(shí),保持對(duì)象鎖,仍然占有該鎖;而wait()睡眠時(shí),釋放對(duì)象鎖。sleep()使當(dāng)前線程進(jìn)入停滯狀態(tài)(阻塞當(dāng)前線程),讓出CPU的使用、目的是不讓當(dāng)前線程獨(dú)自霸占該進(jìn)程所獲的CPU資源,以留一定時(shí)間給其他線程執(zhí)行的機(jī)會(huì);sleep()是Thread類的Static(靜態(tài))的方法;因此他不能改變對(duì)象的機(jī)鎖,所以當(dāng)在一個(gè)Synchronized塊中調(diào)用Sleep()方法是,線程雖然休眠了,但是對(duì)象的鎖并木有被釋放,其他線程無(wú)法訪問(wèn)這個(gè)對(duì)象(即使睡著也持有對(duì)象鎖)。在sleep()休眠時(shí)間期滿后,該線程不一定會(huì)立即執(zhí)行,這是因?yàn)槠渌€程可能正在運(yùn)行而且沒(méi)有被調(diào)度為放棄執(zhí)行,除非此線程具有更高的優(yōu)先級(jí)。
wait()方法是Object類里的方法;當(dāng)一個(gè)線程執(zhí)行到wait()方法時(shí),它就進(jìn)入到一個(gè)和該對(duì)象相關(guān)的等待池中,同時(shí)失去(釋放)了對(duì)象的機(jī)鎖(暫時(shí)失去鎖,wait(long timeout)超時(shí)時(shí)間到后還需要返還對(duì)象鎖);其他線程可以訪問(wèn);
wait()使用notify或者notifyAlll或者指定睡眠時(shí)間來(lái)喚醒當(dāng)前等待池中的線程。wiat()必須放在synchronizedblock中,否則扔出”java.lang.IllegalMonitorStateException“異常。
第四篇:Java AWT編程總結(jié)
1.什么是GUI?
a)GUI是Graphics User Interface的全稱,意思是圖形用戶界面.2.為什么需要GUI?
a)圖形用戶界面能夠讓最終用戶通過(guò)鼠標(biāo)拖動(dòng)、單擊等動(dòng)作就可以操作整個(gè)應(yīng)用,從而提高應(yīng)用的用戶體驗(yàn)效果,使程序受到用戶的歡迎.3.Java通過(guò)AWT和SWING來(lái)完成GUI圖形用戶界面編程.4.AWT
a)AWT是SUN公司提供的一個(gè)基本的GUI類庫(kù),被稱為抽象工具集(Abstract
Window-Toolkit),它為Java應(yīng)用程序提供了基本的組件.b)AWT組件需要調(diào)用運(yùn)行平臺(tái)的圖形界面來(lái)創(chuàng)建和平臺(tái)一致的對(duì)等體,所以AWT只
能使用所有平臺(tái)都支持的公共組件,因此AWT只能夠提供一些 常用的GUI組件.5.AWT的主要組成部分
a)Component,代表一個(gè)具體圖形表示能力的對(duì)象,可以在屏幕上顯示,并與用戶交互.通常我們把它稱為”組件”.b)MenuComponent,代表圖形界面的菜單.i.MenuBar,代表菜單條.ii.Menu,代表一個(gè)菜單項(xiàng)的集合.iii.MenuItem,代表一個(gè)菜單項(xiàng).c)Container,代表一個(gè)AWT組件容器,可以盛裝其他Commponent組件,它繼承自
Component抽象類,本身也代表一個(gè)Component組件.i.Window,可獨(dú)立存在的頂級(jí)窗口.1.Frame,代表一個(gè)窗體.2.Dialog,代表一個(gè)對(duì)話框
a)FileDialog代表一個(gè)文件對(duì)話框,用于打開(kāi)或保存文件.Panel,可容納其他組件,但不能獨(dú)立存在,必須被添加到其他容器中.ii.iii.ScrollPane,帶滾動(dòng)條的容器.d)LayoutManager,布局管理器,表示容器管理其他組件的方式.i.ii.iii.iv.v.vi.FlowLayout,流式布局,類似于Window平臺(tái)記事本的文本布局方式.BorderLayout,邊框布局,只能盛裝5個(gè)組件,這5個(gè)組件分別位于邊框布局容器的東西南北中五個(gè)方位.GridLayout,網(wǎng)格布局,將組件以網(wǎng)格形式顯示在容器中.GridBagLayout,網(wǎng)格包布局,一種較為復(fù)雜的布局管理器,依賴GridBagConstraints來(lái)約束組件.CardLayout,卡片布局,以時(shí)間來(lái)管理容器內(nèi)的組件,將組件看作是一張張卡片,每次顯示最外面一張卡片(組件).BoxLayou,箱式布局,通常與Box容器結(jié)合使用.6.AWT 的事件
a)應(yīng)用程序響應(yīng)用戶的某個(gè)動(dòng)作或請(qǐng)求,如用戶單擊了一下鼠標(biāo),用戶請(qǐng)求關(guān)閉應(yīng)用
程序窗口等.b)AWT編程中,所有事件的處理都必須交給特定的對(duì)象來(lái)完成,我們將這個(gè)特定的對(duì)
象稱為事件監(jiān)聽(tīng)器.c)AWT的事件處理機(jī)制是一種委派式的事件處理方式,通過(guò)將某個(gè)事件監(jiān)聽(tīng)器注冊(cè)
到用戶指定的組件,當(dāng)用戶進(jìn)行某個(gè)操作并觸發(fā)指定事件時(shí),應(yīng)用程序會(huì)自動(dòng)產(chǎn)生一個(gè)事件(Event)對(duì)象并作為參數(shù)傳給事件監(jiān)聽(tīng)器中的事件處理器,然后由事件監(jiān)
聽(tīng)器通知事件處理器來(lái)響應(yīng)用戶,完成用戶的請(qǐng)求.d)不同的事件需要不同的事件監(jiān)聽(tīng)器,不同的監(jiān)聽(tīng)器需要實(shí)現(xiàn)不同的監(jiān)聽(tīng)器接口.e)事件監(jiān)聽(tīng)器接口:為某個(gè)特定事件定義了響應(yīng)用戶請(qǐng)求的方法,當(dāng)用戶將某個(gè)事件
監(jiān)聽(tīng)器注冊(cè)到指定組件上以響應(yīng)特定的事件時(shí),則該事件監(jiān)聽(tīng)器必須實(shí)現(xiàn)對(duì)應(yīng)的事件監(jiān)聽(tīng)器接口才能對(duì)用戶的請(qǐng)求進(jìn)行有效處理.例如,用戶點(diǎn)擊了鼠標(biāo)右鍵,希望打開(kāi)某個(gè)應(yīng)用程序的右鍵菜單,則注冊(cè)到該應(yīng)用程序上的事件監(jiān)聽(tīng)器必須實(shí)現(xiàn)鼠標(biāo)事件監(jiān)聽(tīng)器接口,并實(shí)現(xiàn)該接口內(nèi)部某些方法來(lái)完成用戶的請(qǐng)求.f)事件適配器,很多時(shí)候,我們只需要實(shí)現(xiàn)某個(gè)事件監(jiān)聽(tīng)器接口中個(gè)別方法就能完成應(yīng)用程序的實(shí)際需求,但實(shí)現(xiàn)該事件監(jiān)聽(tīng)器接口的類必須實(shí)現(xiàn)該接口中所有的抽象方法,這會(huì)造成代碼的冗余.而事件適配器可以幫我們解決這個(gè)問(wèn)題,事件適配器實(shí)現(xiàn)了所有的擁有多個(gè)抽象方法的事件監(jiān)聽(tīng)器接口,并空實(shí)現(xiàn)了這些接口中所有的抽象方法,所謂空實(shí)現(xiàn),就是方法中沒(méi)有任何實(shí)現(xiàn)代碼,因此,我們可以通過(guò)繼承對(duì)應(yīng)事件監(jiān)聽(tīng)器接口的事件適配器抽象類,并實(shí)現(xiàn)我們感興趣的方法來(lái)完成應(yīng)用需求即可.g)Java事件處理過(guò)程中主要涉及的三類對(duì)象
i.事件源,通常為普通組件.ii.事件,通常指用戶的某個(gè)操作,如單擊了一下鼠標(biāo),按了一下回車鍵.iii.事件監(jiān)聽(tīng)器,負(fù)責(zé)監(jiān)聽(tīng)事件源上所發(fā)生的事件,并作出響應(yīng).h)AWT事件監(jiān)聽(tīng)器的實(shí)現(xiàn)形式
i.ii.內(nèi)部類形式 頂級(jí)類形式
iii.類本身作為事件監(jiān)聽(tīng)器
iv.匿名內(nèi)部類形式
v.注:目前最為流行的事件監(jiān)聽(tīng)器的實(shí)現(xiàn)形式是內(nèi)部類形式和匿名內(nèi)部類形式.7.AWT繪圖
a)AWT繪圖的實(shí)現(xiàn)過(guò)程.i.重寫畫(huà)布類的paint方法,繪圖圖形.ii.注冊(cè)事件監(jiān)聽(tīng)器到指定的組件.iii.調(diào)用Component類的repaint方法繪制圖形.b)AWT實(shí)現(xiàn)繪圖主要涉及的對(duì)象
i.ii.c)Component類的子類Canvas類,它代表一個(gè)畫(huà)布.Graphics,代表一個(gè)畫(huà)筆,可以在Canvas的子類中繪制用戶自訂的圖形.Image類代表了位圖,它的一個(gè)主要的實(shí)現(xiàn)類BufferedImage是可以訪問(wèn)圖形數(shù)據(jù)
緩沖區(qū),并可以返回一個(gè)Graphics對(duì)象來(lái)繪制該BuuferedImage.d)可以使用ImageIO工具類的ImageReader和ImageWriter讀寫磁盤上的位圖文件.8.AWT的優(yōu)缺點(diǎn)
a)AWT在許多非桌面環(huán)境,如嵌入式設(shè)備中有著自己的優(yōu)勢(shì),它的主要優(yōu)點(diǎn)如下:i.ii.iii.iv.更少的內(nèi)存:對(duì)運(yùn)行在有限環(huán)境中的GUI程序的開(kāi)發(fā),是合適的。2.更少的啟動(dòng)事件:由于AWT組件是本地由操作系統(tǒng)實(shí)現(xiàn)的。絕大多數(shù)的二進(jìn)制代碼已經(jīng)在如系統(tǒng)啟動(dòng)的時(shí)候被預(yù)裝載了,這降低了它的啟動(dòng)事件。3.更好的響應(yīng):由于本地組件由操作系統(tǒng)渲染。4.成熟穩(wěn)定的:能夠正常工作并很少使你的程序崩潰。
b)同樣它也有不少的缺點(diǎn)
i.ii.iii.更少組件類型:表和樹(shù)這些重要的組件缺失了。它們是桌面應(yīng)用程序中普遍使用的。2.缺乏豐富的組件特征:按鈕不支持圖片。3.無(wú)擴(kuò)展性:AWT的組件是本地組件。JVM中的AWT類實(shí)例實(shí)際只是包含本地
組件的引用。唯一的擴(kuò)展點(diǎn)是AWT的Canvas組件,可以從零開(kāi)始創(chuàng)建自定義組
件。然而無(wú)法繼承和重用一個(gè)已有的AWT組件
9.AWT總結(jié):AWT是SUN不推薦使用的工具集,實(shí)際開(kāi)發(fā)中很少使用AWT而是使用SUN公司
和Netscape公司共同開(kāi)發(fā)的一個(gè)新的用戶界面庫(kù)-Swing來(lái)開(kāi)發(fā)GUI應(yīng)用程序,AWT是圖形用戶界面編程的基礎(chǔ),它的布局管理、事件機(jī)制、剪貼板操作等內(nèi)容仍然適用于Swing GUI編程.
第五篇:Java線程編程總結(jié)
線程編程方面
60、java中有幾種方法可以實(shí)現(xiàn)一個(gè)線程?用什么關(guān)鍵字修飾同步方法? stop()和suspend()方法為何不推薦使用?
答:有兩種實(shí)現(xiàn)方法,分別是繼承Thread類與實(shí)現(xiàn)Runnable接口 用synchronized關(guān)鍵字修飾同步方法
反對(duì)使用stop(),是因?yàn)樗话踩K鼤?huì)解除由線程獲取的所有鎖定,而且如果對(duì)象處于一種不連貫狀態(tài),那么其他線程能在那種狀態(tài)下檢查和修改它們。結(jié)果很難檢查出真正的問(wèn)題所在。suspend()方法容易發(fā)生死鎖。調(diào)用suspend()的時(shí)候,目標(biāo)線程會(huì)停下來(lái),但卻仍然持有在這之前獲得的鎖定。此時(shí),其他任何線程都不能訪問(wèn)鎖定的資源,除非被“掛起”的線程恢復(fù)運(yùn)行。對(duì)任何線程來(lái)說(shuō),如果它們想恢復(fù)目標(biāo)線程,同時(shí)又試圖使用任何一個(gè)鎖定的資源,就會(huì)造成死鎖。所以不應(yīng)該使用suspend(),而應(yīng)在自己的Thread類中置入一個(gè)標(biāo)志,指出線程應(yīng)該活動(dòng)還是掛起。若標(biāo)志指出線程應(yīng)該掛起,便用wait()命其進(jìn)入等待狀態(tài)。若標(biāo)志指出線程應(yīng)當(dāng)恢復(fù),則用一個(gè)notify()重新啟動(dòng)線程。61、sleep()和 wait()有什么區(qū)別? 答:sleep是線程類(Thread)的方法,導(dǎo)致此線程暫停執(zhí)行指定時(shí)間,給執(zhí)行機(jī)會(huì)給其他線程,但是監(jiān)控狀態(tài)依然保持,到時(shí)后會(huì)自動(dòng)恢復(fù)。調(diào)用sleep不會(huì)釋放對(duì)象鎖。
wait是Object類的方法,對(duì)此對(duì)象調(diào)用wait方法導(dǎo)致本線程放棄對(duì)象鎖,進(jìn)入等待此對(duì)象的等待鎖定池,只有針對(duì)此對(duì)象發(fā)出notify方法(或notifyAll)后本線程才進(jìn)入對(duì)象鎖定池準(zhǔn)備獲得對(duì)象鎖進(jìn)入運(yùn)行狀態(tài)。
62、同步和異步有何異同,在什么情況下分別使用他們?舉例說(shuō)明。
答:如果數(shù)據(jù)將在線程間共享。例如正在寫的數(shù)據(jù)以后可能被另一個(gè)線程讀到,或者正在讀的數(shù)據(jù)可能已經(jīng)被另一個(gè)線程寫過(guò)了,那么這些數(shù)據(jù)就是共享數(shù)據(jù),必須進(jìn)行同步存取。
當(dāng)應(yīng)用程序在對(duì)象上調(diào)用了一個(gè)需要花費(fèi)很長(zhǎng)時(shí)間來(lái)執(zhí)行的方法,并且不希望讓程序等待方法的返回時(shí),就應(yīng)該使用異步編程,在很多情況下采用異步途徑往往更有效率。63、啟動(dòng)一個(gè)線程是用run()還是start()? 答:?jiǎn)?dòng)一個(gè)線程是調(diào)用start()方法,使線程所代表的虛擬處理機(jī)處于可運(yùn)行狀態(tài),這意味著它可以由JVM調(diào)度并執(zhí)行。這并不意味著線程就會(huì)立即運(yùn)行。run()方法可以產(chǎn)生必須退出的標(biāo)志來(lái)停止一個(gè)線程。
64、當(dāng)一個(gè)線程進(jìn)入一個(gè)對(duì)象的一個(gè)synchronized方法后,其它線程是否可進(jìn)入此對(duì)象的其它方法? 答:不能,一個(gè)對(duì)象的一個(gè)synchronized方法只能由一個(gè)線程訪問(wèn)。
我認(rèn)為:其他線程可以進(jìn)入非synchronized方法,但不能進(jìn)入這個(gè)對(duì)象的synchronized方法。65、請(qǐng)說(shuō)出你所知道的線程同步的方法。
答:wait():使一個(gè)線程處于等待狀態(tài),并且釋放所持有的對(duì)象的lock。
sleep():使一個(gè)正在運(yùn)行的線程處于睡眠狀態(tài),是一個(gè)靜態(tài)方法,調(diào)用此方法要捕捉InterruptedException異常。
notify():喚醒一個(gè)處于等待狀態(tài)的線程,注意的是在調(diào)用此方法的時(shí)候,并不能確切的喚醒某一個(gè)等待狀態(tài)的線程,而是由JVM確定喚醒哪個(gè)線程,而且不是按優(yōu)先級(jí)。
Allnotity():喚醒所有處入等待狀態(tài)的線程,注意并不是給所有喚醒線程一個(gè)對(duì)象的鎖,而是讓它們競(jìng)爭(zhēng)。
66、多線程有幾種實(shí)現(xiàn)方法,都是什么?同步有幾種實(shí)現(xiàn)方法,都是什么? 答:多線程有兩種實(shí)現(xiàn)方法,分別是繼承Thread類與實(shí)現(xiàn)Runnable接口 同步的實(shí)現(xiàn)方面有兩種,分別是synchronized,wait與notify 67、線程的基本概念、線程的基本狀態(tài)以及狀態(tài)之間的關(guān)系
答:線程指在程序執(zhí)行過(guò)程中,能夠執(zhí)行程序代碼的一個(gè)執(zhí)行單位,每個(gè)程序至少都有一個(gè)線程,也就是程序本身。
Java中的線程有四種狀態(tài)分別是:運(yùn)行、就緒、掛起、結(jié)束
68、簡(jiǎn)述synchronized和java.util.concurrent.locks.Lock的異同 ? 答:主要相同點(diǎn):Lock能完成synchronized所實(shí)現(xiàn)的所有功能
主要不同點(diǎn):Lock有比synchronized更精確的線程語(yǔ)義和更好的性能。synchronized會(huì)自動(dòng)釋放鎖,而Lock一定要求程序員手工釋放,并且必須在finally從句中釋放。
Jsp方面
69、forward 和redirect的區(qū)別
答:forward是服務(wù)器請(qǐng)求資源,服務(wù)器直接訪問(wèn)目標(biāo)地址的URL,把那個(gè)URL的響應(yīng)內(nèi)容讀取過(guò)來(lái),然后把這些內(nèi)容再發(fā)給瀏覽器,瀏覽器根本不知道服務(wù)器發(fā)送的內(nèi)容是從哪兒來(lái)的,所以它的地址欄中還是原來(lái)的地址。
redirect就是服務(wù)端根據(jù)邏輯,發(fā)送一個(gè)狀態(tài)碼,告訴瀏覽器重新去請(qǐng)求那個(gè)地址,一般來(lái)說(shuō)瀏覽器會(huì)用剛才請(qǐng)求的所有參數(shù)重新請(qǐng)求,所以session,request參數(shù)都可以獲取。70、jsp有哪些內(nèi)置對(duì)象?作用分別是什么?
答:JSP共有以下9種基本內(nèi)置組件(可與ASP的6種內(nèi)部組件相對(duì)應(yīng)):
request 用戶端請(qǐng)求,此請(qǐng)求會(huì)包含來(lái)自GET/POST請(qǐng)求的參數(shù)
response 網(wǎng)頁(yè)傳回用戶端的回應(yīng)
pageContext 網(wǎng)頁(yè)的屬性是在這里管理
session 與請(qǐng)求有關(guān)的會(huì)話期
application servlet 正在執(zhí)行的內(nèi)容
out 用來(lái)傳送回應(yīng)的輸出 config servlet的構(gòu)架部件
page JSP網(wǎng)頁(yè)本身
exception 針對(duì)錯(cuò)誤網(wǎng)頁(yè),未捕捉的例外
71、jsp有哪些動(dòng)作?作用分別是什么? 答:JSP共有以下6種基本動(dòng)作
jsp:include:在頁(yè)面被請(qǐng)求的時(shí)候引入一個(gè)文件。
jsp:useBean:尋找或者實(shí)例化一個(gè)JavaBean。
jsp:setProperty:設(shè)置JavaBean的屬性。
jsp:getProperty:輸出某個(gè)JavaBean的屬性。
jsp:forward:把請(qǐng)求轉(zhuǎn)到一個(gè)新的頁(yè)面。
jsp:plugin:根據(jù)瀏覽器類型為Java插件生成OBJECT或EMBED標(biāo)記 72、JSP中動(dòng)態(tài)INCLUDE與靜態(tài)INCLUDE的區(qū)別?
答:動(dòng)態(tài)INCLUDE用jsp:include動(dòng)作實(shí)現(xiàn)
靜態(tài)INCLUDE用include偽碼實(shí)現(xiàn),定不會(huì)檢查所含文件的變化,適用于包含靜態(tài)頁(yè)面
<%@ include file=“included.htm” %> 73、兩種跳轉(zhuǎn)方式分別是什么?有什么區(qū)別? 答:有兩種,分別為:
74、JSP的內(nèi)置對(duì)象及方法。
答:request表示HttpServletRequest對(duì)象。它包含了有關(guān)瀏覽器請(qǐng)求的信息,并且提供了幾個(gè)用于獲取cookie, header, 和session數(shù)據(jù)的有用的方法。
response表示HttpServletResponse對(duì)象,并提供了幾個(gè)用于設(shè)置送回 瀏覽器的響應(yīng)的方法(如cookies,頭信息等)
out對(duì)象是javax.jsp.JspWriter的一個(gè)實(shí)例,并提供了幾個(gè)方法使你能用于向?yàn)g覽器回送輸出結(jié)果。
pageContext表示一個(gè)javax.servlet.jsp.PageContext對(duì)象。它是用于方便存取各種范圍的名字空間、servlet相關(guān)的對(duì)象的API,并且包裝了通用的servlet相關(guān)功能的方法。
session表示一個(gè)請(qǐng)求的javax.servlet.http.HttpSession對(duì)象。Session可以存貯用戶的狀態(tài)信息
applicaton 表示一個(gè)javax.servle.ServletContext對(duì)象。這有助于查找有關(guān)servlet引擎和servlet環(huán)境的信息
config表示一個(gè)javax.servlet.ServletConfig對(duì)象。該對(duì)象用于存取servlet實(shí)例的初始化參數(shù)。page表示從該頁(yè)面產(chǎn)生的一個(gè)servlet實(shí)例
Servlet方面
75、說(shuō)一說(shuō)Servlet的生命周期?
答:servlet有良好的生存期的定義,包括加載和實(shí)例化、初始化、處理請(qǐng)求以及服務(wù)結(jié)束。這個(gè)生存期由javax.servlet.Servlet接口的init,service和destroy方法表達(dá)。Servlet被服務(wù)器實(shí)例化后,容器運(yùn)行其init方法,請(qǐng)求到達(dá)時(shí)運(yùn)行其service方法,service方法自動(dòng)派遣運(yùn)行與請(qǐng)求對(duì)應(yīng)的doXXX方法(doGet,doPost)等,當(dāng)服務(wù)器決定將實(shí)例銷毀的時(shí)候調(diào)用其destroy方法。
與cgi的區(qū)別在于servlet處于服務(wù)器進(jìn)程中,它通過(guò)多線程方式運(yùn)行其service方法,一個(gè)實(shí)例可以服務(wù)于多個(gè)請(qǐng)求,并且其實(shí)例一般不會(huì)銷毀,而CGI對(duì)每個(gè)請(qǐng)求都產(chǎn)生新的進(jìn)程,服務(wù)完成后就銷毀,所以效率上低于servlet。
76、JAVA SERVLET API中forward()與redirect()的區(qū)別?
答:前者僅是容器中控制權(quán)的轉(zhuǎn)向,在客戶端瀏覽器地址欄中不會(huì)顯示出轉(zhuǎn)向后的地址;后者則是完全的跳轉(zhuǎn),瀏覽器將會(huì)得到跳轉(zhuǎn)的地址,并重新發(fā)送請(qǐng)求鏈接。這樣,從瀏覽器的地址欄中可以看到跳轉(zhuǎn)后的鏈接地址。所以,前者更加高效,在前者可以滿足需要時(shí),盡量使用forward()方法,并且,這樣也有助于隱藏實(shí)際的鏈接。在有些情況下,比如,需要跳轉(zhuǎn)到一個(gè)其它服務(wù)器上的資源,則必須使用sendRedirect()方法。77、Servlet的基本架構(gòu) 答:
public class ServletName extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { } public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { } }
78、什么情況下調(diào)用doGet()和doPost()?
答:Jsp頁(yè)面中的form標(biāo)簽里的method屬性為get時(shí)調(diào)用doGet(),為post時(shí)調(diào)用doPost()。79、servlet的生命周期
答:web容器加載servlet,生命周期開(kāi)始。通過(guò)調(diào)用servlet的init()方法進(jìn)行servlet的初始化。通過(guò)調(diào)用service()方法實(shí)現(xiàn),根據(jù)請(qǐng)求的不同調(diào)用不同的do***()方法。結(jié)束服務(wù),web容器調(diào)用servlet的destroy()方法。
80、如何現(xiàn)實(shí)servlet的單線程模式 答:<%@ page isThreadSafe=“false”%> 81、頁(yè)面間對(duì)象傳遞的方法
答:request,session,application,cookie等
82、JSP和Servlet有哪些相同點(diǎn)和不同點(diǎn),他們之間的聯(lián)系是什么?
答:JSP是Servlet技術(shù)的擴(kuò)展,本質(zhì)上是Servlet的簡(jiǎn)易方式,更強(qiáng)調(diào)應(yīng)用的外表表達(dá)。JSP編譯后是“類servlet”。Servlet和JSP最主要的不同點(diǎn)在于,Servlet的應(yīng)用邏輯是在Java文件中,并且完全從表示層中的HTML里分離開(kāi)來(lái)。而JSP的情況是Java和HTML可以組合成一個(gè)擴(kuò)展名為.jsp的文件。JSP側(cè)重于視圖,Servlet主要用于控制邏輯。83、四種會(huì)話跟蹤技術(shù)
答:會(huì)話作用域ServletsJSP 頁(yè)面描述
page否是代表與一個(gè)頁(yè)面相關(guān)的對(duì)象和屬性。一個(gè)頁(yè)面由一個(gè)編譯好的 Java servlet 類(可以帶有任何的 include 指令,但是沒(méi)有 include 動(dòng)作)表示。這既包括 servlet 又包括被編譯成 servlet 的 JSP 頁(yè)面
request是是代表與 Web 客戶機(jī)發(fā)出的一個(gè)請(qǐng)求相關(guān)的對(duì)象和屬性。一個(gè)請(qǐng)求可能跨越多個(gè)頁(yè)面,涉及多個(gè) Web 組件(由于 forward 指令和 include 動(dòng)作的關(guān)系)
session是是代表與用于某個(gè) Web 客戶機(jī)的一個(gè)用戶體驗(yàn)相關(guān)的對(duì)象和屬性。一個(gè) Web 會(huì)話可以也經(jīng)常會(huì)跨越多個(gè)客戶機(jī)請(qǐng)求
application是是代表與整個(gè) Web 應(yīng)用程序相關(guān)的對(duì)象和屬性。這實(shí)質(zhì)上是跨越整個(gè) Web 應(yīng)用程序,包括多個(gè)頁(yè)面、請(qǐng)求和會(huì)話的一個(gè)全局作用域 84、Request對(duì)象的主要方法 答:
setAttribute(String name,Object):設(shè)置名字為name的request的參數(shù)值 getAttribute(String name):返回由name指定的屬性值
getAttributeNames():返回request對(duì)象所有屬性的名字集合,結(jié)果是一個(gè)枚舉的實(shí)例 getCookies():返回客戶端的所有Cookie對(duì)象,結(jié)果是一個(gè)Cookie數(shù)組 getCharacterEncoding():返回請(qǐng)求中的字符編碼方式 getContentLength():返回請(qǐng)求的Body的長(zhǎng)度
getHeader(String name):獲得HTTP協(xié)議定義的文件頭信息 getHeaders(String name):返回指定名字的request Header的所有值,結(jié)果是一個(gè)枚舉的實(shí)例 getHeaderNames():返回所以request Header的名字,結(jié)果是一個(gè)枚舉的實(shí)例 getInputStream():返回請(qǐng)求的輸入流,用于獲得請(qǐng)求中的數(shù)據(jù) getMethod():獲得客戶端向服務(wù)器端傳送數(shù)據(jù)的方法
getParameter(String name):獲得客戶端傳送給服務(wù)器端的有name指定的參數(shù)值
getParameterNames():獲得客戶端傳送給服務(wù)器端的所有參數(shù)的名字,結(jié)果是一個(gè)枚舉的實(shí)例 getParameterValues(String name):獲得有name指定的參數(shù)的所有值 getProtocol():獲取客戶端向服務(wù)器端傳送數(shù)據(jù)所依據(jù)的協(xié)議名稱 getQueryString():獲得查詢字符串
getRequestURI():獲取發(fā)出請(qǐng)求字符串的客戶端地址 getRemoteAddr():獲取客戶端的IP地址 getRemoteHost():獲取客戶端的名字
getSession([Boolean create]):返回和請(qǐng)求相關(guān)Session getServerName():獲取服務(wù)器的名字
getServletPath():獲取客戶端所請(qǐng)求的腳本文件的路徑 getServerPort():獲取服務(wù)器的端口號(hào)
removeAttribute(String name):刪除請(qǐng)求中的一個(gè)屬性
85、我們?cè)趙eb應(yīng)用開(kāi)發(fā)過(guò)程中經(jīng)常遇到輸出某種編碼的字符,如iso8859-1等,如何輸出一個(gè)某種編碼的字符串? 答:
Public String translate(String str){ String tempStr = “";try { tempStr = new String(str.getBytes(”ISO-8859-1“), ”GBK");tempStr = tempStr.trim();} catch(Exception e){ System.err.println(e.getMessage());} return tempStr;} 86、Servlet執(zhí)行時(shí)一般實(shí)現(xiàn)哪幾個(gè)方法? 答:
public void init(ServletConfig config)public ServletConfig getServletConfig()public String getServletInfo()public void service(ServletRequest request,ServletResponse response)public void destroy()
Jdbc、Jdo方面88、Jdo是什么?
87、Class.forName的作用?為什么要用?
答:調(diào)用該訪問(wèn)返回一個(gè)以字符串指定類名的類的對(duì)象。答:JDO是Java對(duì)象持久化的新的規(guī)范,為java data object的簡(jiǎn)稱,也是一個(gè)用于存取某種數(shù)據(jù)倉(cāng)庫(kù)中的對(duì)象的標(biāo)準(zhǔn)化API。JDO提供了透明的對(duì)象存儲(chǔ),因此對(duì)開(kāi)發(fā)人員來(lái)說(shuō),存儲(chǔ)數(shù)據(jù)對(duì)象完全不需要額外的代碼(如JDBC API的使用)。這些繁瑣的例行工作已經(jīng)轉(zhuǎn)移到JDO產(chǎn)品提供商身上,使開(kāi)發(fā)人員解脫出來(lái),從而集中時(shí)間和精力在業(yè)務(wù)邏輯上。另外,JDO很靈活,因?yàn)樗梢栽谌魏螖?shù)據(jù)底層上運(yùn)行。JDBC只是面向關(guān)系數(shù)據(jù)庫(kù)(RDBMS)JDO更通用,提供到任何數(shù)據(jù)底層的存儲(chǔ)功能,比如關(guān)系數(shù)據(jù)庫(kù)、文件、XML以及對(duì)象數(shù)據(jù)庫(kù)(ODBMS)等等,使得應(yīng)用可移植性更強(qiáng)。89、說(shuō)出數(shù)據(jù)連接池的工作機(jī)制是什么? 答:J2EE服務(wù)器啟動(dòng)時(shí)會(huì)建立一定數(shù)量的池連接,并一直維持不少于此數(shù)目的池連接。客戶端程序需要連接時(shí),池驅(qū)動(dòng)程序會(huì)返回一個(gè)未使用的池連接并將其表記為忙。如果當(dāng)前沒(méi)有空閑連接,池驅(qū)動(dòng)程序就新建一定數(shù)量的連接,新建連接的數(shù)量有配置參數(shù)決定。當(dāng)使用的池連接調(diào)用完成后,池驅(qū)動(dòng)程序?qū)⒋诉B接表記為空閑,其他調(diào)用就可以使用這個(gè)連接。90、Jdo是什么? 答:JDO是Java對(duì)象持久化的新的規(guī)范,為java data object的簡(jiǎn)稱,也是一個(gè)用于存取某種數(shù)據(jù)倉(cāng)庫(kù)中的對(duì)象的標(biāo)準(zhǔn)化API。JDO提供了透明的對(duì)象存儲(chǔ),因此對(duì)開(kāi)發(fā)人員來(lái)說(shuō),存儲(chǔ)數(shù)據(jù)對(duì)象完全不需要額外的代碼(如JDBC API的使用)。這些繁瑣的例行工作已經(jīng)轉(zhuǎn)移到JDO產(chǎn)品提供商身上,使開(kāi)發(fā)人員解脫出來(lái),從而集中時(shí)間和精力在業(yè)務(wù)邏輯上。另外,JDO很靈活,因?yàn)樗梢栽谌魏螖?shù)據(jù)底層上運(yùn)行。JDBC只是面向關(guān)系數(shù)據(jù)庫(kù)(RDBMS)JDO更通用,提供到任何數(shù)據(jù)底層的存儲(chǔ)功能,比如關(guān)系數(shù)據(jù)庫(kù)、文件、XML以及對(duì)象數(shù)據(jù)庫(kù)(ODBMS)等等,使得應(yīng)用可移植性更強(qiáng)。
Xml方面
91、xml有哪些解析技術(shù)?區(qū)別是什么? 答:有DOM,SAX,STAX等
DOM:處理大型文件時(shí)其性能下降的非常厲害。這個(gè)問(wèn)題是由DOM的樹(shù)結(jié)構(gòu)所造成的,這種結(jié)構(gòu)占用的內(nèi)存較多,而且DOM必須在解析文件之前把整個(gè)文檔裝入內(nèi)存,適合對(duì)XML的隨機(jī)訪問(wèn)。
SAX:不現(xiàn)于DOM,SAX是事件驅(qū)動(dòng)型的XML解析方式。它順序讀取XML文件,不需要一次全部裝載整個(gè)文件。當(dāng)遇到像文件開(kāi)頭,文檔結(jié)束,或者標(biāo)簽開(kāi)頭與標(biāo)簽結(jié)束時(shí),它會(huì)觸發(fā)一個(gè)事件,用戶通過(guò)在其回調(diào)事件中寫入處理代碼來(lái)處理XML文件,適合對(duì)XML的順序訪問(wèn) STAX:Streaming API for XML(StAX)92、你在項(xiàng)目中用到了xml技術(shù)的哪些方面?如何實(shí)現(xiàn)的?
答:用到了數(shù)據(jù)存貯,信息配置兩方面。在做數(shù)據(jù)交換平臺(tái)時(shí),將不能數(shù)據(jù)源的數(shù)據(jù)組裝成XML文件,然后將XML文件壓縮打包加密后通過(guò)網(wǎng)絡(luò)傳送給接收者,接收解密與解壓縮后再同XML文件中還原相關(guān)信息進(jìn)行處理。在做軟件配置時(shí),利用XML可以很方便的進(jìn)行,軟件的各種配置參數(shù)都存貯在XML文件中。
93、XML文檔定義有幾種形式?它們之間有何本質(zhì)區(qū)別?解析XML文檔有哪幾種方式? 答:a: 兩種形式 dtd schema,b: 本質(zhì)區(qū)別:schema本身是xml的,可以被XML解析器解析(這也是從DTD上發(fā)展schema的根本目的),c:有DOM,SAX,STAX等
DOM:處理大型文件時(shí)其性能下降的非常厲害。這個(gè)問(wèn)題是由DOM的樹(shù)結(jié)構(gòu)所造成的,這種結(jié)構(gòu)占用的內(nèi)存較多,而且DOM必須在解析文件之前把整個(gè)文檔裝入內(nèi)存,適合對(duì)XML的隨機(jī)訪問(wèn)
SAX:不現(xiàn)于DOM,SAX是事件驅(qū)動(dòng)型的XML解析方式。它順序讀取XML文件,不需要一次全部裝載整個(gè)文件。當(dāng)遇到像文件開(kāi)頭,文檔結(jié)束,或者標(biāo)簽開(kāi)頭與標(biāo)簽結(jié)束時(shí),它會(huì)觸發(fā)一個(gè)事件,用戶通過(guò)在其回調(diào)事件中寫入處理代碼來(lái)處理XML文件,適合對(duì)XML的順序訪問(wèn) STAX:Streaming API for XML(StAX)