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