<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 154  文章 - 60  trackbacks - 0
    <2007年11月>
    28293031123
    45678910
    11121314151617
    18192021222324
    2526272829301
    2345678

    聲明:

    該blog是為了收集資料,認(rèn)識(shí)朋友,學(xué)習(xí)、提高技術(shù),所以本blog的內(nèi)容除非聲明,否則一律為轉(zhuǎn)載!!

    感謝那些公開自己技術(shù)成果的高人們!!!

    支持開源,尊重他人的勞動(dòng)!!

    常用鏈接

    留言簿(3)

    隨筆分類(148)

    隨筆檔案(143)

    收藏夾(2)

    其他

    學(xué)習(xí)(技術(shù))

    觀察思考(非技術(shù))

    搜索

    •  

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    多線程編程——基礎(chǔ)篇 (一)
    時(shí)間:2006-08-08
    作者:axman
    [寫在前面]

      隨著計(jì)算機(jī)技術(shù)的發(fā)展,編程模型也越來越復(fù)雜多樣化。但多線程編程模型是目前計(jì)算機(jī)系統(tǒng)架構(gòu)的最終模型。隨著CPU主頻的不斷攀升,X86架構(gòu)的硬件已經(jīng)成為瓶,在這種架構(gòu)的CPU主頻最高為4G。事實(shí)上目前3.6G主頻的CPU已經(jīng)接近了頂峰。

      如果不能從根本上更新當(dāng)前CPU的架構(gòu)(在很長(zhǎng)一段時(shí)間內(nèi)還不太可能),那么繼續(xù)提高CPU性能的方法就是超線程CPU模式。那么,作業(yè)系統(tǒng)、應(yīng)用程序要發(fā)揮CPU的最大性能,就是要改變到以多線程編程模型為主的并行處理系統(tǒng)和并發(fā)式應(yīng)用程序。

      所以,掌握多線程編程模型,不僅是目前提高應(yīng)用性能的手段,更是下一代編程模型的核心思想。多線程編程的目的,就是"最大限度地利用CPU資源",當(dāng)某一線程的處理不需要占用CPU而只和I/O,OEMBIOS等資源打交道時(shí),讓需要占用CPU資源的其它線程有機(jī)會(huì)獲得CPU資源。從根本上說,這就是多線程編程的最終目的。

      [第一需要弄清的問題]

      如同程序和進(jìn)程的區(qū)別,要掌握多線程編程,第一要弄清的問題是:線程對(duì)象和線程的區(qū)別

      線程對(duì)象是可以產(chǎn)生線程的對(duì)象。比如在java平臺(tái)中Thread對(duì)象,Runnable對(duì)象。線程,是指正在執(zhí)行的一個(gè)指點(diǎn)令序列。在java平臺(tái)上是指從一個(gè)線程對(duì)象的start()開始,運(yùn)行run方法體中的那一段相對(duì)獨(dú)立的過程。

      鑒于作者的水平,無法用更確切的詞匯來描述它們的定義。但這兩個(gè)有本質(zhì)區(qū)別的概念請(qǐng)初學(xué)者細(xì)細(xì)體會(huì),隨著介紹的深入和例程分析的增加,就會(huì)慢慢明白它們所代表的真實(shí)含義。

      天下難事必始于易,天下大事必始于細(xì)。

      讓我們先從最簡(jiǎn)單的"單線程"來入手:(1)帶引號(hào)說明只是相對(duì)而言的單線程,(2)基于java。

        class BeginClass{
    public static void main(String[] args){
    for(int i=0;i<100;i++)
    System.out.println("Hello,World!");
    }
    }
    

      如果我們成功編譯了該java文件,然后在命令行上敲入:

      java BeginClass

      現(xiàn)在發(fā)生了什么呢?每一個(gè)java程序員,從他開始學(xué)習(xí)java的第一分鐘里都會(huì)接觸到這個(gè)問

      題,但是,你知道它到底發(fā)生發(fā)什么?

      JVM進(jìn)程被啟動(dòng),在同一個(gè)JVM進(jìn)程中,有且只有一個(gè)進(jìn)程,就是它自己。然后在這個(gè)JVM環(huán)境中,所有程序的運(yùn)行都是以線程來運(yùn)行。JVM最先會(huì)產(chǎn)生一個(gè)主線程,由它來運(yùn)行指定程序的入口點(diǎn)。在這個(gè)程序中,就是主線程從main方法開始運(yùn)行。當(dāng)main方法結(jié)束后,主線程運(yùn)行完成。JVM進(jìn)程也隨之退出。

      我們看到的是一個(gè)主線程在運(yùn)行main方法,這樣的只有一個(gè)線程執(zhí)行程序邏輯的流程我們稱

      之為單線程。這是JVM提供給我們的單線程環(huán)境,事實(shí)上,JVM底層還至少有垃圾回收這樣的后臺(tái)線程以及其它非java線程,但這些線程對(duì)我們而言不可訪問,我們只認(rèn)為它是單線程的。

      主線程是JVM自己?jiǎn)?dòng)的,在這里它不是從線程對(duì)象產(chǎn)生的。在這個(gè)線程中,它運(yùn)行了main方法這個(gè)指令序列。理解它,但它沒有更多可以研究的內(nèi)容。

      [接觸多線程]

        class MyThread extends Thread{
    public void run(){
    System.out.println("Thread say:Hello,World!");
    }
    }
    public class MoreThreads{
    public static void main(String[] args){
    new MyThread();
    new MyThread().start();
    System.out.println("Main say:Hello,World");
    }
    }
    

      執(zhí)行這個(gè)程序,main方法第一行產(chǎn)生了一個(gè)線程對(duì)象,但并沒有線程啟動(dòng)。

      main方法第二行產(chǎn)生了一個(gè)線程對(duì)象,并啟動(dòng)了一個(gè)線程。

      main方法第三行,產(chǎn)生并啟動(dòng)一個(gè)線程后,主線程自己也繼續(xù)執(zhí)行其它語句。

      我們先不研究Thread對(duì)象的具體內(nèi)容,稍微來回想一下上面的兩個(gè)概念,線程對(duì)象線程。在JAVA中,線程對(duì)象是JVM產(chǎn)生的一個(gè)普通的Object子類。而線程是CPU分配給這個(gè)對(duì)象的一個(gè)運(yùn)行過程。我們說的這個(gè)線程在干什么,不是說一個(gè)線程對(duì)象在干什么,而是這個(gè)運(yùn)行過程在干什么。如果一時(shí)想不明白,不要急,但你要記得它們不是一回事就行了。

      累了吧?為不么不繼續(xù)了?

      基于這種風(fēng)格來介紹多線程,并不是每個(gè)人都喜歡和接受的,如果你不喜歡,正好不浪費(fèi)你的時(shí)間了,而如果你接受的話,那就看下一節(jié)吧。


    ========================================================================
    多線程編程——基礎(chǔ)篇 (二)

    時(shí)間:2006-08-16
    作者:axman
    在進(jìn)入java平臺(tái)的線程對(duì)象之前,基于基礎(chǔ)篇(一)的一些問題,我先插入兩個(gè)基本概念。

    [線程的并發(fā)與并行]

      在單CPU系統(tǒng)中,系統(tǒng)調(diào)度在某一時(shí)刻只能讓一個(gè)線程運(yùn)行,雖然這種調(diào)試機(jī)制有多種形式(大多數(shù)是時(shí)間片輪巡為主),但無論如何,要通過不斷切換需要運(yùn)行的線程讓其運(yùn)行的方式就叫并發(fā)(concurrent)。而在多CPU系統(tǒng)中,可以讓兩個(gè)以上的線程同時(shí)運(yùn)行,這種可以同時(shí)讓兩個(gè)以上線程同時(shí)運(yùn)行的方式叫做并行(parallel)

      在上面包括以后的所有論述中,請(qǐng)各位朋友諒解,我無法用最準(zhǔn)確的詞語來定義儲(chǔ)如并發(fā)和并行這類術(shù)語,但我以我的經(jīng)驗(yàn)?zāi)芡ㄋ椎馗嬖V大家它是怎么一回事,如果您看到我說的一些"標(biāo)準(zhǔn)"文檔上說的不一樣,只要意思一致,那您就不要挑刺了。

    [JAVA線程對(duì)象]

      現(xiàn)在我們來開始考察JAVA中線程對(duì)象。

      在JAVA中,要開始一個(gè)線程,有兩種方式。一是直接調(diào)用Thread實(shí)例的start()方法,二是
    將Runable實(shí)例傳給一個(gè)Thread實(shí)例然后調(diào)用它的start()方法。

      在前面已經(jīng)說過,線程對(duì)象和線程是兩個(gè)完全不同的概念。這里我們?cè)俅紊钊胍幌拢梢粋€(gè)線程的實(shí)例,并不代表啟動(dòng)了線程。而啟動(dòng)線程是說在某個(gè)線程對(duì)象上啟動(dòng)了該實(shí)例對(duì)應(yīng)的線程,當(dāng)該線程結(jié)束后,并不會(huì)就立即消失。

      對(duì)于從很多書籍上可以看到的基礎(chǔ)知識(shí)我就不用多說了。既然是基礎(chǔ)知識(shí),我也著重于從普通文檔上讀不到的內(nèi)容。所以本節(jié)我重點(diǎn)要說的是兩種線程對(duì)象產(chǎn)生線程方式的區(qū)別。

    class MyThread extends Thread{
    public int x = 0;
    public void run(){
    for(int i=0;i<100;i++){
    try{
    Thread.sleep(10);
    }catch(Exception e){}
    System.out.println(x++);
    }
    }
    }
    

      如果我們生成MyThread的一個(gè)實(shí)例,然后調(diào)用它的start()方法,那么就產(chǎn)生了這個(gè)實(shí)例對(duì)應(yīng)的線程:

    public class Test {
    public static void main(String[] args) throws Exception{
    MyThread mt = new MyThread();
    mt.start();
    }
    }
    

      不用說,最終會(huì)打印出0到99,現(xiàn)在我們稍微玩一點(diǎn)花樣:

    public class Test {
    public static void main(String[] args) throws Exception{
    MyThread mt = new MyThread();
    mt.start();
    System.out.println(101);
    }
    }
    

      也不用說,在基礎(chǔ)篇(一)中我們知道由于單CPU的原因,一般會(huì)先打印101,然后打印0到99。不過我們可以控制線程讓它按我們的意思來運(yùn)行:

    public class Test {
    public static void main(String[] args) throws Exception{
    MyThread mt = new MyThread();
    mt.start();
    mt.join();
    System.out.println(101);
    }
    }
    

      好了,我們終于看到,mt實(shí)例對(duì)應(yīng)的線程(假如我有時(shí)說mt線程請(qǐng)你不要怪我,不過我盡量不這么說)。在運(yùn)行完成后,主線程才打印101。因?yàn)槲覀冏尞?dāng)前線程(這里是主線程)等待mt線程的運(yùn)行結(jié)束。"在線程對(duì)象a上調(diào)用join()方法,就是讓當(dāng)前正在執(zhí)行的線程等待線程對(duì)象a對(duì)應(yīng)的線程運(yùn)行完成后才繼續(xù)運(yùn)行。" 請(qǐng)大家一定要深刻理解并熟記這句話,而我這里引出這個(gè)知識(shí)點(diǎn)的目的是為了讓你繼續(xù)看下面的例子:

    public class Test {
    public static void main(String[] args) throws Exception{
    MyThread mt = new MyThread();
    mt.start();
    mt.join();
    Thread.sleep(3000);
    mt.start();
    }
    }
    

      當(dāng)線程對(duì)象mt運(yùn)行完成后,我們讓主線程休息一下,然后我們?cè)俅卧谶@個(gè)線程對(duì)象上啟動(dòng)線程。結(jié)果我們看到:

      Exception in thread "main" java.lang.IllegalThreadStateException

      也就是這種線程對(duì)象一時(shí)運(yùn)行一次完成后,它就再也不能運(yùn)行第二次了。我們可以看一下它有具體實(shí)現(xiàn):

        public synchronized void start() {
    if (started)
    throw new IllegalThreadStateException();
    started = true;
    group.add(this);
    start0();
    }
    

      一個(gè)Thread的實(shí)例一旦調(diào)用start()方法,這個(gè)實(shí)例的started標(biāo)記就標(biāo)記為true,事實(shí)中不管這個(gè)線程后來有沒有執(zhí)行到底,只要調(diào)用了一次start()就再也沒有機(jī)會(huì)運(yùn)行了,這意味著:

    [通過Thread實(shí)例的start(),一個(gè)Thread的實(shí)例只能產(chǎn)生一個(gè)線程]

      那么如果要在一個(gè)實(shí)例上產(chǎn)生多個(gè)線程(也就是我們常說的線程池),我們應(yīng)該如何做呢?這就是Runnable接口給我們帶來的偉大的功能。

    class R implements Runnable{
    private int x = 0;
    public void run(){
    for(int i=0;i<100;i++){
    try{
    Thread.sleep(10);
    }catch(Exception e){}
    System.out.println(x++);
    }
    }
    }
    

      
    正如它的名字一樣,Runnable的實(shí)例是可運(yùn)行的,但它自己并不能直接運(yùn)行,它需要被Thread對(duì)象來包裝才行運(yùn)行:

    public class Test {
    public static void main(String[] args) throws Exception{
    new Thread(new R()).start();
    }
    }
    

      當(dāng)然這個(gè)結(jié)果和mt.start()沒有什么區(qū)別。但如果我們把一個(gè)Runnable實(shí)例給Thread對(duì)象多次包裝,我們就可以看到它們實(shí)際是在同一實(shí)例上啟動(dòng)線程:

    public class Test {
    public static void main(String[] args) throws Exception{
    R r = new R();
    for(int i=0;i<10;i++)
    new Thread(r).start();
    }
    }
    

      x是實(shí)例對(duì)象,但結(jié)果是x被加到了999,說明這10個(gè)線程是在同一個(gè)r對(duì)象上運(yùn)行的。請(qǐng)大家注意,因?yàn)檫@個(gè)例子是在單CPU上運(yùn)行的,所以沒有對(duì)多個(gè)線程同時(shí)操作共同的對(duì)象進(jìn)行同步。這里是為了說明的方便而簡(jiǎn)化了同步,而真正的環(huán)境中你無法預(yù)知程序會(huì)在什么環(huán)境下運(yùn)行,所以一定要考慮同步。

      到這里我們做一個(gè)完整的例子來說明線程產(chǎn)生的方式不同而生成的線程的區(qū)別:

    package debug;
    import java.io.*;
    import java.lang.Thread;
    class MyThread extends Thread{
    public int x = 0;
    public void run(){
    System.out.println(++x);
    }
    }
    class R implements Runnable{
    private int x = 0;
    public void run(){
    System.out.println(++x);
    }
    }
    public class Test {
    public static void main(String[] args) throws Exception{
    for(int i=0;i<10;i++){
    Thread t = new MyThread();
    t.start();
    }
    Thread.sleep(10000);//讓上面的線程運(yùn)行完成
    R r = new R();
    for(int i=0;i<10;i++){
    Thread t = new Thread(r);
    t.start();
    }
    }
    }
    

      上面10個(gè)線程對(duì)象產(chǎn)生的10個(gè)線程運(yùn)行時(shí)打印了10次1。下面10個(gè)線程對(duì)象產(chǎn)生的10個(gè)線程運(yùn)行時(shí)打印了1到10。我們把下面的10個(gè)線程稱為同一實(shí)例(Runnable實(shí)例)的多個(gè)線程

      下節(jié)我們將研究線程對(duì)象方法,還是那句話,一般文檔中可以讀到的內(nèi)容我不會(huì)介紹太多
    請(qǐng)大家自己了解。


    ========================================================================
    多線程編程——基礎(chǔ)篇 (三)

    時(shí)間:2006-08-22
    作者:axman

    線程對(duì)象的幾個(gè)重要的方法

      盡管線程對(duì)象的常用方法可以通過API文檔來了解,但是有很多方法僅僅從API說明是無法詳細(xì)了解的。

      本來打算用一節(jié)的篇幅來把線程方法中一些重要的知識(shí)說完,但這樣下來估計(jì)要很常的篇幅,可能要用好幾節(jié)才能說把和線程方法相關(guān)的一些重要的知識(shí)說完。

      首先我們接基礎(chǔ)篇(二)來說明start()方法。

      一個(gè)線程對(duì)象生成后,如果要產(chǎn)生一個(gè)執(zhí)行的線程,就一定要調(diào)用它的start()方法.在介紹這個(gè)方法時(shí)不得不同時(shí)說明run方法.其實(shí)線程對(duì)象的run方法完全是一個(gè)接口回調(diào)方法,它是你這個(gè)線程對(duì)象要完成的具體邏輯.簡(jiǎn)單說你要做什么就你在run中完成,而如何做,什么時(shí)候做就不需要你控制了,你只要調(diào)用start()方法,JVM就會(huì)管理這個(gè)線程對(duì)象讓它產(chǎn)生一個(gè)線程并注冊(cè)到線程處理系統(tǒng)中。

      從表面上看,start()方法調(diào)用了run()方法,事實(shí)上,start()方法并沒有直接調(diào)用run方法.在JDK1.5以前start()方法是本地方法,它如何最終調(diào)用run方法已經(jīng)不是JAVA程序員所能了解的.而在JDK1.5中,原來的那個(gè)本地start()方法被start0()代替,另個(gè)一個(gè)純JAVA的start()中調(diào)用本地方法start0(),而在start()方法中做了一個(gè)驗(yàn)證,就是對(duì)一個(gè)全局變量(對(duì)象變量)started做檢驗(yàn),如果為true,則start()拋出異常,不會(huì)調(diào)用本地方法start0(),否則,先將該變量設(shè)有true,然后調(diào)用start0()。

      從中我們可以看到這個(gè)為了控制一個(gè)線程對(duì)象只能運(yùn)行成功一次start()方法.這是因?yàn)榫€程的運(yùn)行要獲取當(dāng)前環(huán)境,包括安全,父線程的權(quán)限,優(yōu)先級(jí)等條件,如果一個(gè)線程對(duì)象可以運(yùn)行多次,那么定義一個(gè)static 的線程在一個(gè)環(huán)境中獲取相應(yīng)權(quán)限和優(yōu)先級(jí),運(yùn)行完成后它在另一個(gè)環(huán)境中利用原來的權(quán)限和優(yōu)先級(jí)等屬性在當(dāng)前環(huán)境中運(yùn)行,這樣就造成無法預(yù)知的結(jié)果.簡(jiǎn)單說來,讓一個(gè)線程對(duì)象只能成功運(yùn)行一次,是基于對(duì)線程管理的需要。

    start()方法最本質(zhì)的功能是從CPU中申請(qǐng)另一個(gè)線程空間來執(zhí)行run()方法中的代碼,它和當(dāng)前的線程是兩條線,在相對(duì)獨(dú)立的線程空間運(yùn)行,也就是說,如果你直接調(diào)用線程對(duì)象的run()方法,當(dāng)然也會(huì)執(zhí)行,但那是在當(dāng)前線程中執(zhí)行,run()方法執(zhí)行完成后繼續(xù)執(zhí)行下面的代碼.而調(diào)用start()方法后,run()方法的代碼會(huì)和當(dāng)前線程并發(fā)(單CPU)或并行(多CPU)執(zhí)行。

      所以請(qǐng)記住一句話[調(diào)用線程對(duì)象的run方法不會(huì)產(chǎn)生一個(gè)新的線程],雖然可以達(dá)到相同的執(zhí)行結(jié)果,但執(zhí)行過程和執(zhí)行效率不同。

      [線程的interrupt()方法,interrupted()和isInterrupted()]

      這三個(gè)方法是關(guān)系非常密切而且又比較復(fù)雜的,雖然它們各自的功能很清楚,但它們之間的關(guān)系有大多數(shù)人不是真正的了解。

      先說interrupt()方法,它是實(shí)例方法,而它也是最奇怪的方法,在java語言中,線程最初被設(shè)計(jì)為"隱晦難懂"的東西,直到現(xiàn)在它的語義不沒有象它的名字那樣準(zhǔn)確。大多數(shù)人以為,一個(gè)線程象調(diào)用了interrupt()方法,那它對(duì)應(yīng)的線程就應(yīng)該被中斷而拋出異常,事實(shí)中,當(dāng)一個(gè)線程對(duì)象調(diào)用interrupt()方法,它對(duì)應(yīng)的線程并沒有被中斷,只是改變了它的中斷狀態(tài)。

      使當(dāng)前線程的狀態(tài)變以中斷狀態(tài),如果沒有其它影響,線程還會(huì)自己繼續(xù)執(zhí)行。

      只有當(dāng)線程執(zhí)行到sleep,wait,join等方法時(shí),或者自己檢查中斷狀態(tài)而拋出異常的情況下,線程才會(huì)拋出異常。

    如果線程對(duì)象調(diào)用interrupt()后它對(duì)應(yīng)的線程就立即中斷,那么interrupted()方法就不可能執(zhí)行。

       因?yàn)閕nterrupted()方法是一個(gè)static方法,就是說只能在當(dāng)前線程上調(diào)用,而如果一個(gè)線程interrupt()后它已經(jīng)中斷了,那它又如何讓自己interrupted()?

      正因?yàn)橐粋€(gè)線程調(diào)用interrupt()后只是改變了中斷狀態(tài),它可以繼續(xù)執(zhí)行下去,在沒有調(diào)用sleep,wait,join等法或自己拋出異常之前,它就可以調(diào)用interrupted()來清除中斷狀態(tài)(還會(huì)原狀)interrupted()方法會(huì)檢查當(dāng)前線程的中斷狀態(tài),如果為 "被中斷狀態(tài)"則改變當(dāng)前線程為"非中斷狀態(tài)"并返回true,如果為"非中斷狀態(tài)"則返回false,它不僅檢查當(dāng)前線程是否為中斷狀態(tài),而且在保證當(dāng)前線程回來非中斷狀態(tài),所以它叫"interrupted",是說中斷的狀態(tài)已經(jīng)結(jié)束(到非中斷狀態(tài)了)isInterrupted()方法則僅僅檢查線程對(duì)象對(duì)應(yīng)的線程是否是中斷狀態(tài),并不改變它的狀態(tài)。

      目前大家只能先記住這三個(gè)方法的功能,只有真正深入到多線程編程實(shí)踐中,才會(huì)體會(huì)到它們?yōu)槭裁词菍?duì)象方法,為什么是類方法。

      線程到底什么時(shí)候才被中斷拋出InterruptedException異常,我們將在提高篇中詳細(xì)討論。

      [sleep(),join(),yield()方法]

      在現(xiàn)在的環(huán)節(jié)中,我只能先說明這些方法的作用和調(diào)用原則,至于為什么,在基礎(chǔ)篇中無法深入,只能在提高篇中詳細(xì)說明。

      sleep()方法中是類方法,也就是對(duì)當(dāng)前線程而言的,程序員不能指定某個(gè)線程去sleep,只能是當(dāng)前線程執(zhí)行到sleep()方法時(shí),睡眠指定的時(shí)間(讓其它線程運(yùn)行).事實(shí)上也只能是類方法,在當(dāng)前線程上調(diào)用.試想如果你調(diào)用一個(gè)線程對(duì)象的sleep()方法,那么這個(gè)對(duì)象對(duì)應(yīng)的線程如果不是正在運(yùn)行,它如何sleep()?所以只有當(dāng)前線程,因?yàn)樗趫?zhí)行,你才能保證它可以調(diào)用sleep()方法。

      原則:[在同步方法中盡量不要調(diào)用線程的sleep()方法],或者簡(jiǎn)單說,對(duì)于一般水平的程序員你基本不應(yīng)該調(diào)用sleep()方法。

      join()方法,正如第一節(jié)所言,在一個(gè)線程對(duì)象上調(diào)用join方法,是當(dāng)前線程等待這個(gè)線程對(duì)象對(duì)應(yīng)的線程結(jié)束,比如有兩個(gè)工作,工作A要耗時(shí)10秒鐘,工作B要耗時(shí)10秒或更多。我們?cè)诔绦蛑邢壬梢粋€(gè)線程去做工作B,然后做工作A。

      new?B().start();//做工作B

      A();//做工作A

      工作A完成后,下面要等待工作B的結(jié)果來進(jìn)行處理.如果工作B還沒有完成我就不能進(jìn)行下面的工作C,所以

      B?b?=?new?B();

      b.start();//做工作B

      A();//做工作A

      b.join();//等工作B完成。

      C();//繼續(xù)工作C。

      原則:[join是測(cè)試其它工作狀態(tài)的唯一正確方法],我見過很多人,甚至有的是博士生,在處理一項(xiàng)工作時(shí)如果另一項(xiàng)工作沒有完成,說讓當(dāng)前工作線程sleep(x),我問他,你這個(gè)x是如何指定的,你怎么知道是100毫秒而不是99毫秒或是101毫秒?其實(shí)這就是OnXXX事件的實(shí)質(zhì),我們不是要等多長(zhǎng)時(shí)間才去做什么事,而是當(dāng)?shù)却墓ぷ髡猛瓿傻臅r(shí)候去做。

      yield()方法也是類方法,只在當(dāng)前線程上調(diào)用,理由同上,它主是讓當(dāng)前線程放棄本次分配到的時(shí)間片原則:[不是非常必要的情況下,沒有理由調(diào)用它].調(diào)用這個(gè)方法不會(huì)提高任何效率,只是降低了CPU的總周期上面介紹的線程一些方法,基于(基礎(chǔ)篇)而言只能簡(jiǎn)單提及.以后具體應(yīng)用中我會(huì)結(jié)合實(shí)例詳細(xì)論述。

      線程本身的其它方法請(qǐng)參看API文檔.下一節(jié)介紹非線程的方法,但和線程密切相關(guān)的兩[三]個(gè)對(duì)象方法:

      [wait(),notify()/notifyAll()]

      這是在多線程中非常重要的方法。


    =========================================================================
    多線程編程 ——  基礎(chǔ)篇 (四)

    時(shí)間:2006-08-31
    作者:axman

    [wait(),notify()/notityAll()方法]

      關(guān)于這兩個(gè)方法,有很多的內(nèi)容需要說明.在下面的說明中可能會(huì)有很多地方不能一下子明白,但在看完本節(jié)后,即使不能完全明白,你也一定要回過頭來記住下面的兩句話:

      [wait(),notify()/notityAll()方法是普通對(duì)象的方法(Object超類中實(shí)現(xiàn)),而不是線程對(duì)象的方法]

      [wait(),notify()/notityAll()方法只能在同步方法中調(diào)用]

    [線程的互斥控制]

      多個(gè)線程同時(shí)操作某一對(duì)象時(shí),一個(gè)線程對(duì)該對(duì)象的操作可能會(huì)改變其狀態(tài),而該狀態(tài)會(huì)影響另一線程對(duì)該對(duì)象的真正結(jié)果.

      這個(gè)例子我們?cè)谔嗟奈臋n中可以看到,就象兩個(gè)操售票員同時(shí)售出同一張票一樣.

    線程A 線程B
    1.線程A在數(shù)據(jù)庫中查詢存票,發(fā)現(xiàn)票C可以賣出  
    class="left"2.線程A接受用戶訂票請(qǐng)求,準(zhǔn)備出票.  
      3.這時(shí)切換到了線程B執(zhí)行
      4.線程B在數(shù)據(jù)庫中查詢存票,發(fā)現(xiàn)票C可以賣出
      5.線程B將票賣了出去
    6.切換到線程A執(zhí)行,線程A賣了一張已經(jīng)賣出的票  

      所以需要一種機(jī)制來管理這類問題的發(fā)生,當(dāng)某個(gè)線程正在執(zhí)行一個(gè)不可分割的部分時(shí),其它線程不能不能同時(shí)執(zhí)行這一部分.

      象這種控制某一時(shí)刻只能有一個(gè)線程執(zhí)行某個(gè)執(zhí)行單元的機(jī)制就叫互斥控制或共享互斥(mutual exclusion)

      在JAVA中,用synchornized關(guān)鍵字來實(shí)現(xiàn)互斥控制(暫時(shí)這樣認(rèn)為,JDK1.5已經(jīng)發(fā)展了新的機(jī)制)

    [synchornized關(guān)鍵字]

      把一個(gè)單元聲明為synchornized,就可以讓在同一時(shí)間只有一個(gè)線程操作該方法.

      有人說synchornized就是一把鎖,事實(shí)上它確實(shí)存在鎖,但是是誰的鎖,鎖誰,這是一個(gè)非常復(fù)雜的問題.

      每個(gè)對(duì)象只有一把監(jiān)視鎖(monitor lock),一次只能被一個(gè)線程獲取.當(dāng)一個(gè)線程獲取了這一個(gè)鎖后,其它線程就只能等待這個(gè)線程釋放鎖才能再獲取.

    那么synchornized關(guān)鍵字到底鎖什么?得到了誰的鎖?

    對(duì)于同步塊,synchornized獲取的是參數(shù)中的對(duì)象鎖:

    synchornized(obj){
    //...............
    }

      線程執(zhí)行到這里時(shí),首先要獲取obj這個(gè)實(shí)例的鎖,如果沒有獲取到線程只能等待.如果多個(gè)線程執(zhí)行到這里,只能有一個(gè)線程獲取obj的鎖,然后執(zhí)行{}中的語句,所以,obj對(duì)象的作用范圍不同,控制程序不同.

      假如:

    public void test(){
    Object o = new Object();
    synchornized(obj){
    //...............
    }
    }
    

      這段程序控制不了任何,多個(gè)線程之間執(zhí)行到Object o = new Object();時(shí)會(huì)各自產(chǎn)生一個(gè)對(duì)象然后獲取這個(gè)對(duì)象有監(jiān)視鎖,各自皆大歡喜地執(zhí)行.

      而如果是類的屬性:

    class Test{
    Object o = new Object();
    public void test(){
    synchornized(o){
    //...............
    }
    }
    }

      所有執(zhí)行到Test實(shí)例的synchornized(o)的線程,只有一個(gè)線程可以獲取到監(jiān)視鎖.

      有時(shí)我們會(huì)這樣:

    public void test(){
    synchornized(this){
    //...............
    }
    }

      那么所有執(zhí)行Test實(shí)例的線程只能有一個(gè)線程執(zhí)行.而synchornized(o)和synchornized(this)的范圍是不同的,因?yàn)閳?zhí)行到Test實(shí)例的synchornized(o)的線程等待時(shí),其它線程可以執(zhí)行Test實(shí)例的synchornized(o1)部分,但多個(gè)線程同時(shí)只有一個(gè)可以執(zhí)行Test實(shí)例的synchornized(this).]

      而對(duì)于

    synchornized(Test.class){
    //...............
    }

      這樣的同步塊而言,所有調(diào)用Test多個(gè)實(shí)例的線程賜教只能有一個(gè)線程可以執(zhí)行.

    [synchornized方法]

      如果一個(gè)方法聲明為synchornized的,則等同于把在為個(gè)方法上調(diào)用synchornized(this).

      如果一個(gè)靜態(tài)方法被聲明為synchornized,則等同于把在為個(gè)方法上調(diào)用synchornized(類.class).

      現(xiàn)在進(jìn)入wait方法和notify/notifyAll方法.這兩個(gè)(或叫三個(gè))方法都是Object對(duì)象的方法,而不是線程對(duì)象的方法.如同鎖一樣,它們是在線程中調(diào)用某一對(duì)象上執(zhí)行的.

    class Test{
    public synchornized void test(){
    //獲取條件,int x 要求大于100;
    if(x < 100)
    wait();
    }
    }

      這里為了說明方法沒有加在try{}catch(){}中,如果沒有明確在哪個(gè)對(duì)象上調(diào)用wait()方法,則為this.wait();

      假如:

      Test t = new Test();

      現(xiàn)在有兩個(gè)線程都執(zhí)行到t.test();方法.其中線程A獲取了t的對(duì)象鎖,進(jìn)入test()方法內(nèi).

      這時(shí)x小于100,所以線程A進(jìn)入等待.

      當(dāng)一個(gè)線程調(diào)用了wait方法后,這個(gè)線程就進(jìn)入了這個(gè)對(duì)象的休息室(waitset),這是一個(gè)虛擬的對(duì)象,但JVM中一定存在這樣的一個(gè)數(shù)據(jù)結(jié)構(gòu)用來記錄當(dāng)前對(duì)象中有哪些程線程在等待.

      當(dāng)一個(gè)線程進(jìn)入等待時(shí),它就會(huì)釋放鎖,讓其它線程來獲取這個(gè)鎖.

      所以線程B有機(jī)會(huì)獲得了線程A釋放的鎖,進(jìn)入test()方法,如果這時(shí)x還是小于100,線程B也進(jìn)入了t的休息室.

      這兩個(gè)線程只能等待其它線程調(diào)用notity[All]來喚醒.

      但是如果調(diào)用的是有參數(shù)的wait(time)方法,則線程A,B都會(huì)在休息室中等待這個(gè)時(shí)間后自動(dòng)喚醒.

    [為什么真正的應(yīng)用都是用while(條件)而不用if(條件)]

      在實(shí)際的編程中我們看到大量的例子都是用?

      while(x < 100)

      wait();go();而不是用if,為什么呢?

      在多個(gè)線程同時(shí)執(zhí)行時(shí),if(x <100)是不安全的.因?yàn)槿绻€程A和線程B都在t的休息室中等待,這時(shí)另一個(gè)線程使x==100了,并調(diào)用notifyAll方法,線程A繼續(xù)執(zhí)行下面的go().而它執(zhí)行完成后,x有可能又小于100,比如下面的程序中調(diào)用了--x,這時(shí)切換到線程B,線程B沒有繼續(xù)判斷,直接執(zhí)行g(shù)o();就產(chǎn)生一個(gè)錯(cuò)誤的條件,只有while才能保證線程B又繼續(xù)檢查一次.

    [notify/notifyAll方法]

      這兩個(gè)方法都是把某個(gè)對(duì)象上休息區(qū)內(nèi)的線程喚醒,notify只能喚醒一個(gè),但究竟是哪一個(gè)不能確定,而notifyAll則喚醒這個(gè)對(duì)象上的休息室中所有的線程.

      一般有為了安全性,我們?cè)诮^對(duì)多數(shù)時(shí)候應(yīng)該使用notifiAll(),除非你明確知道只喚醒其中的一個(gè)線程.

      那么是否是只要調(diào)用一個(gè)對(duì)象的wait()方法,當(dāng)前線程就進(jìn)入了這個(gè)對(duì)象的休息室呢?事實(shí)中,要調(diào)用一個(gè)對(duì)象的wait()方法,只有當(dāng)前線程獲取了這個(gè)對(duì)象的鎖,換句話說一定要在這個(gè)對(duì)象的同步方法或以這個(gè)對(duì)象為參數(shù)的同步塊中.

    class MyThread extends Thread{
    Test t = new Test();
    public void run(){
    t.test();
    System.out.println("Thread say:Hello,World!");
    }
    }
    public class Test {
    int x = 0;
    public  void test(){
    if(x==0)
    try{
    wait();
    }catch(Exception e){}
    }
    public static void main(String[] args) throws Exception{
    new MyThread().start();
    }
    }
    

      這個(gè)線程就不會(huì)進(jìn)入t的wait方法而直接打印出Thread say:Hello,World!.

      而如果改成:

    public class Test {
    int x = 0;
    public synchornized void test(){
    if(x==0)
    try{
    wait();
    }catch(Exception e){}
    }
    public static void main(String[] args) throws Exception{
    new MyThread().start();
    }
    }

      我們就可以看到線程一直等待,注意這個(gè)線程進(jìn)入等待后沒有其它線程喚醒,除非強(qiáng)行退出JVM環(huán)境,否則它一直等待.

      所以請(qǐng)記住:

      [線程要想調(diào)用一個(gè)對(duì)象的wait()方法就要先獲得該對(duì)象的監(jiān)視鎖,而一旦調(diào)用wait()后又立即釋放該鎖]

      以上是對(duì)線程基礎(chǔ)知識(shí)的簡(jiǎn)單介紹,不進(jìn)入實(shí)例,我們無法真正了解它的真實(shí)意義.下節(jié)我們就會(huì)以實(shí)例來進(jìn)入多線程編程的 實(shí)戰(zhàn)篇

    轉(zhuǎn)載自dev2dev網(wǎng)友a(bǔ)xman的go deep into java專欄。



    posted on 2007-11-09 13:37 lk 閱讀(318) 評(píng)論(0)  編輯  收藏 所屬分類: j2se
    主站蜘蛛池模板: 国产91色综合久久免费| 亚洲美女一区二区三区| 亚洲精品123区在线观看| 久久aa毛片免费播放嗯啊| 免费一级黄色毛片| 亚洲欧洲av综合色无码| 国内精自视频品线六区免费| 亚洲精品无码鲁网中文电影| 国产av无码专区亚洲av毛片搜| 噜噜嘿在线视频免费观看| 亚洲制服丝袜一区二区三区| 蜜桃AV无码免费看永久| 亚洲第一福利网站| 久久免费国产精品一区二区| 中文字幕精品亚洲无线码二区 | 亚洲国产老鸭窝一区二区三区 | 在线免费一区二区| 亚洲乱码av中文一区二区| 男人的好看免费观看在线视频| 亚洲国产高清人在线| 最近免费mv在线电影| 亚洲六月丁香六月婷婷色伊人| 久久99国产综合精品免费| 亚洲婷婷综合色高清在线| 国产精品1024永久免费视频| 激情综合亚洲色婷婷五月APP| 免费做爰猛烈吃奶摸视频在线观看| 中文字幕亚洲男人的天堂网络 | 好吊色永久免费视频大全 | 亚洲欧洲国产综合| 日韩免费一级毛片| 182tv免费视频在线观看| 亚洲欧洲国产经精品香蕉网| 毛片免费在线播放| 免费无码av片在线观看| 亚洲乱码一二三四区麻豆| 免费人成视网站在线观看不卡| 久久精品免费一区二区三区| 亚洲无mate20pro麻豆| 亚洲人妻av伦理| 99在线精品视频观看免费|