糯米文學吧

位置:首頁 > 計算機 > java語言

Java多線程知識點

java語言2.85W

引導語;多線程是指從軟件或者硬件上實現多個線程併發執行的技術。以下是本站小編分享給大家的Java多線程知識點,歡迎閲讀!

Java多線程知識點

  1.1多線程的概念

多線程編程的含義是你可將程序任務分成幾個並行的子任務。特別是在網絡編程中,你會發現很多功能是可以併發執行的。比如網絡傳輸速度較慢,用户輸入速度較慢,你可以用兩個獨立的線程去完成這?copy;功能,而不影響正常的顯示或其他功能。多線程是與單線程比較而言的,普通的WINDOWS採用單線程程序結構,其工作原理是:主程序有一個消息循環,不斷從消息隊列中讀入消息來決定下一步所要乾的事情,一般是一個子函數,只有等這個子函數執行完返回後,主程序才能接收另外的消息來執行。比如子函數功能是在讀一個網絡數據,或讀一個文件,只有等讀完這?copy;數據或文件才能接收下一個消息。在執行這個子函數過程中你什麼也不能幹。但往往讀網絡數據和等待用户輸入有很多時間處於等待狀態,多線程利用這個特點將任務分成多個併發任務後,就可以解決這個問題。

  1.1.1Java線程的模型

Java的設計思想是建立在當前大多數操作系統都實現了線程調度。Java虛擬機的很多任務都依賴線程調度,而且所有的類庫都是為多線程設計的。實時上,Java支持Macintosh和Ms-dos的平台?reg;所以遲遲未出來就是因為這兩個平台都不支持多線程。Java利用多線程實現了整個執行環境是異步的。在Java程序裏沒有主消息循環。如果一個線程等待讀取網絡數據,它可以運行但不停止系統的其他線程執行。用於處理用户輸入的線程大多時間是等待用户敲鍵盤或擊鼠標。你還可以使動畫的每一幀?reg;間停頓一秒而並不使系統暫停。一?copy;線程啟動後,它可以被掛起,暫時不讓它執行。掛起的線程可以重新恢復執行。任何時間線程都可以被停止,被停止的線程就不能再重新啟動。Java語言裏,線程表現為線程類,線程類封裝了所有需要的線程操作控制。在你心裏,必須很清晰地區分開線程對象和運行線程,你可以將線程對象看作是運行線程的控制面板。在線程對象裏有很多函數來控制一個線程是否運行,睡眠,掛起或停止。線程類是控制線程行為的唯一的手段。一?copy;一個Java程序啟動後,就已經有一個線程在運行。你可通過調用Thread.currentThread函數來查看當前運行的是哪一個線程。

你得到一個線程的控制柄,你就可以作很有趣的事情,即使單線程也一樣。下面這個例子讓你知道怎樣操縱當前線程。Filename:testthread

classtestthread{publicstaticvoidmain(Stringargs[]){Threadt

=Thread.currentThread();t.setName("ThisThreadisrunning");

System.out.println("Therunningthread:"+t);try{for(inti=0;i

{System.out.println("Sleeptime"+i);Thread.sleep(1000);}

}catch(InterruptedExceptione){System.out.println("threadhaswrong");}

}}

執行結果:javatestthreadTherunningthread:Thread[ThisThreadisrunning,5,main]Sleeptime0Sleeptime1Sleeptime2Sleeptime3Sleeptime4

  1.1.2啟動接口

一個線程並不激動人心,多個線程才有實際意義。我們怎樣創建更多的線程呢?我們需要創建線程類的另一個實例。當我們構造了線程類的一個新的實例,我們必須告訴它在新的線程裏應執行哪一段程序。你可以在任意實現了啟動接口的對象上啟動一個線程。啟動接口是一個抽象接口,來表示本對象有一?copy;函數想異步執行。要實現啟動接口,一個類只需要有一個叫run的函數。下面是創建一個新線程的例子:

Filename:twothread.java

classtwothreadimplementsRunnable{twothread(){Threadt1

=Thread.currentThread();t1.setName("Thefirstmainthread");

System.out.println("Therunningthread:"+t1);Threadt2=new

Thread(this,"thesecondthread");System.out.println("creatanother

thread");t2.start();try{System.out.println("firstthreadwill

sleep");Thread.sleep(3000);}catch(InterruptedExceptione)

{System.out.println("firstthreadhaswrong");}

System.out.println("firstthreadexit");}publicvoidrun(){try{for

(inti=0;i

Thread.sleep(1000);}

}catch(InterruptedExceptione){System.out.println("threadhas

wrong");}

System.out.println("secondthreadexit");}publicstaticvoid

main(Stringargs[]){newtwothread();}}

執行結果:javatwothread

Therunningthread:Thread[Thefirstmainthread,5,main]creatanother

threadfirstthreadwillsleepSleeptimeforthread2:0Sleeptimefor

thread2:1Sleeptimeforthread2:2firstthreadexitSleeptimefor

thread2:3Sleeptimeforthread2:4secondthreadexit

main線程用newThread(this,"thesecondthread")創建了一個Thread對象,通過傳遞第一個參數來標明新線程來調用this對象的run函數。然後我們調用start函數,它將使線程從run函數開始執行。

  1.1.3同步

因為多線程給你提?copy;了程序的異步執行的功能,所以在必要時必須還提?copy;一種同步機制。例如,你想兩個線程通訊並共享一個複雜的數據結構,你需要一種機制讓他們相互牽制並正確執行。為這個目的,Java用一種叫監視器(monitor)的機制實現了進程間的異步執行。可以將監視器看作是一個很小的盒子,它只能容納一個線程。一?copy;一個線程進入一個監視器,所有其他線程必須等到第一個線程退出監視器後才能進入。這?copy;監視器可以設計成保護共享的數據不被多個線程同時操作。大多數多線程系統將這?copy;監視器設計成對象,Java提?copy;了一種更清晰的解決方案。沒有Monitor類;每個對象通過將他們的成員函數定義成synchronized來定義自己的顯式監視器,一?copy;一個線程執行在一個synchronized函數裏,其他任何線程都不能調用同一個對象的

synchronized函數。

  1.1.4消息

你的程序被分成幾個邏輯線程,你必須清晰的'知道這?copy;線程?reg;間應怎樣相互通訊。Java提了wait和notify等功能來使線程?reg;間相互交談。一個線程可以進入某一個對象的synchronized函數進入等待狀態,直到其他線程顯式地將它喚醒。可以有多個線程進入同一個函數並等待同一個喚醒消息。

  1.2Java線程例子

  1.2.1顯式定義線程

在我們的單線程應用程序裏,我們並沒有看見線程,因為Java能自動創建和控制你的線程。如果你使用了理解Java語言的瀏覽器,你就已經看到使用多線程的Java程序了。你也許注意到兩個小程序可以同時運行,或在你移動滾動條時小程序繼續執行。這並不是表明小程序是多線程的,但説明這個瀏覽器是多線程的。多線程應用程序(或applet)可以使用好幾個執行上下文來完成它們的工

作。多線程利用了很多任務包含單獨的可分離的子任務的特點。每一個線程完成一個子任務。但是,每一個線程完成子任務時還是順序執行的。一個多線程程序允許各個線程儘快執行完它們。這種特點會有更好的實時輸入反應。

  1.2.2多線程例子

下面這個例子創建了三個單獨的線程,它們分別打印自己的"HelloWorld":

//Defineoursimplethreads.Theywillpauseforashorttime//andthen

printouttheirnamesanddelaytimesclassTestThreadextendsThread

{privateStringwhoami;privateintdelay;

//Ourconstructortostorethename(whoami)//andtimetosleep(delay)

publicTestThread(Strings,intd){whoami=s;delay=d;}

//Run-thethreadmethodsimilartomain()//Whenrunisfinished,the

threaddies.//Runiscalledfromthestart()methodofThreadpublicvoid

run(){//Trytosleepforthespecifiedtimetry{sleep(delay);}

catch(InterruptedExceptione){}//Nowprintoutourname

System.out.println("HelloWorld!"+whoami+""+delay);}}/***Multimtest.

Asimplemultithreadthestprogram*/publicclassmultitest{public

staticvoidmain(Stringargs[]){TestThreadt1,t2,t3;//Createourtest

threadst1=newTestThread("Thread1",(int)(Math.readom()*2000));t2=

newTestThread("Thread2",(int)(Math.readom()*2000));t3=new

TestThread("Thread3",(int)(Math.readom()*2000));

//Starteachofthethreadst1.start();t2.start();t3.start();}}

  1.2.3啟動一個線程

程序啟動時總是調用main()函數,因此main()是我們創建和啟動線程的地方:

t1=newTestThread("Thread1",(int)(Math.readom()*2000));

這一行創建了一個新的線程。後面的兩個參數傳遞了線程的名稱和線程在打印信息?reg;前的延時時間。因為我們直接控制線程,我們必須直接啟動它:t1.start();

  1.2.4操作線程

如果創建線程正常,t1應包含一個有效的執行線程。我們在線程的run()函數裏控制線程。一?copy;我們進入run()函數,我們便可執行裏面的任何程序。run()好象main()一樣。

run()執行完,這個線程也就結束了。在這個例子裏,我們試着延遲一個隨機的時間(通過參數傳遞?)sleep(delay);

sleep()函數只是簡單地告訴線程休息多少個毫秒時間。

如果你想推遲一個線程的執行,你應使用sleep()函數。當線程睡眠是sleep()並不佔用系統資源。其它線程可繼續工作。一?copy;延遲時間完畢,它將打印"HelloWorld"和線程名稱及延遲時間。

  1.2.5暫停一個線程

我們經常需要掛起一個線程而不指定多少時間。例如,如果你創建了一個含有動畫線程的小程序。也許你讓用户暫停動畫至到他們想恢復為止。你並不想將動畫線程仍調,但想讓它停止。象這種類似的線程你可用suspend()函數來控制:t1.suspend();這個函數並不永久地停止了線程,你還可用resume()函數重新激活線程:t1.resume();

  1.2.6停止一個線程

線程的最後一個控制是停止函數stop()。我們用它來停止線程的執行:t1.stop();

注意:這並沒有消滅這個線程,但它停止了線程的執行。並且這個線程不能用t1.start()重新啟動。在我們的例子裏,我們從來不用顯式地停止一個線程。我們只簡單地讓它執行完而已。很多複雜的線程例子將需要我們控制每一個線程。在這種情況下會使用到stop()函數。如果需要,你可以測試你的線程是否被激活。一個線程已經啟動而且沒有停止被認為是激活的。t1.isAlive()如果t1是激活的,這個函數將返回true.

  1.2.1動畫例子

下面是一個包含動畫線程的applet例子:

importjava.awt.*;importjava.awt.image.ImageProducer;import

java.applet.Applet;

publicclassatest3extendsAppletimplementsRunnable{Imageimages[];

MediaTrackertracker;intindex=0;Threadanimator;

intmaxWidth,maxHeight;//Ouroff-screencomponentsfordoublebuffering.

ImageoffScrImage;GraphicsoffScrGC;

//Canwepaintyes?booleanloaded=false;

//Initializetheapplet.Setoursizeandloadtheimagespublicvoidinit()

[//Setupourimagemonitortracker=newMediaTracker(this);

//SetthesizeandwidthofourappletmaxWidth=100;maxHeight=100;

images=newImage[10];//Setupthedouble-bufferandresizeourapplet

try{offScrImage=createImage(maxWidth,maxHeight);offScrGC=

offScrImage.getGraphics();offScrGC.setColor(Color.lightGray);

offScrGC.fillRect(0,0,maxWidth,maxHeight);

resize(maxWidth,maxHeight);}catch(Exceptione)

{e.printStackTrace();}

//loadtheanimationimagesintoanarrayfor(inti=0;i

imageFile=newString("images/Duke/T"+String.valueOf(i+1)+".gif");

images[i]=getImage(getDocumentBase(),imageFile)://Registerthis

imagewiththetrackertracker.addImage(images[i],i);}try{//Use

trackertomakesurealltheimagesareloadedtracker.waitForAll();}

catch(InterruptedExceptione){}loaded=true;}

//Paintthecurrentframe.publicvoidpaint(Graphicsg){if(loaded)

{g.drawImage(offScrImage,0,0,this);}}

//Start,setupourfirstimagepublicvoidstart(){if(tracker.checkID

(index)){offScrGC.drawImage(images[index],0,0,this);}animator=new

Thread(this);animator.start();}

//Run,dotheanimationworkhere.//Grabanimage,pause,grabthenext...

publicvoidrun(){//GettheidofthecurrentthreadThreadme=

Thread.currentThread();

//Ifouranimatorthreadexist,andisthecurrentthread...while

((animatr!=null)&&(animator==me)){if(tracker.checkID(index))

{//Clearthebackgroundandgetthenextimage

offScrGC.fillRect(0,0,100,100);

offScrGCdrawImage(images[index],0,0,this);index++;//Loopbacktothe

beginningandkeepgoingif(index>=images.length){index=0;}}

//Delayheresoanimationlooksnormaltry{animator.sleep(200);}catch

(InterruptedExceptione){}//Drawthenextframerepaint();}}}

  1.3多線程間的通訊

  1.3.1生產者和消費者

多線程的一個重要特點是它們?reg;間可以互相通訊。你可以設計線程使用公用對象,每個線程都可以獨立操作公用對象。典型的線程間通訊建立在生產者和消費者模型上:一個線程產生輸出;另一個線程使用輸入buffer

讓我們創建一個簡單的"AlphabetSoup"生產者和相應的消費者.

  1.3.2生產者

生產者將從thread類裏派生:classProducerextendsThread

{privateSoupsoup;privateStringalphabet="

ABCDEFGHIJKLMNOPQRSTUVWXYZ";

publicProducer(Soups){//Keepourowncopyofthesharedobjectsoup

=s;}

publicvoidrun(){charc;//Throw10lettersintothesoupfor(int

i=0;i

soup.add(c);//printarecordofosraddition

System.out.println("Added"+c+"tothesoup.");//waitabitbeforewe

addthenextlettertry{sleep((int)(Math.random()*1000));}catch

(InterruptedExceptione){}}}}

注意我們創建了Soup類的一個實例。生產者用soup.add()函數來建立字符池。

  1.3.3消費者

讓我們看看消費者的程序:classConsumerextendsThread{privateSoupsoup;

publicConsumer(Soups){//keepourowncopyofthesharedobjectsoup

=s;}

publicvoidrun(){charc;//Eat10lettersfromthealphabetsoupfor

(intI=0;i

letterthatweretrievedSystem.out.println("Atealetter:"+c);//try

{sleep((int)(Math.raddom()*2000));}catch(InterruptedExceptione){}}}}

同理,象生產者一樣,我們用soup.eat()來處理信息。那麼,Soup類到底幹什麼呢?

  1.3.4監視

Soup類執行監視兩個線程?reg;間傳輸信息的功能。監視是多線程中不可缺少的一部分,因為它保持了通訊的流?copy;。讓我們看看Soup.java文件:classSoup{privatecharbuffer[]=newchar[6];privateintnext=0;//Flagstokeeptrackof

ourbufferstatusprivatebooleanisFull=false;privatebooleanisEmpty

=true;publicsyschronizedchareat(){//Wecan'teatifthereisn'tanything

inthebufferwhile(isEmpty==true){try{wait();//we'llexitthis

whenisEmptyturnsfalse}catch(InterruptedExceptione){}}//decrement

thecount,sincewe'regoingtoeatoneletternext--;//Didweeatthe

lastletter?if(next==0){isEmpty=true;}//Weknowthebuffercan't

befull,becausewejustateisFull=false;notify();//returntheletter

tothethreadthatiseatingreturn(buffer[next]);}

//methodtoaddletterstothebufferpublicsynchronizedvoidadd(char

c){//Waitarounduntilthere'sroomtoaddanotherletterwhile(isFull

==true){try{wait();//ThiswillexitwhenisFullturnsfalse}catch

(InterruptedExceptione){}}//addthelettertothenextavailablespot

buffer[next]=c;//Changethenextavailablespotnext++;//Arewefull;

if(next==6){isFull=true;}isEmpty=false;notify();}}soup類包含兩個重要特徵:數據成員buffer[]是私有的,功能成員add()和eat()是公有的。

數據私有避免了生產者和消費者直接獲得數據。直接訪問數據可能造成錯誤。例如,如果消費者企圖從空緩衝區裏取出數據,你將得到不必要的異常,否則,你只能鎖住進程。同步訪問方法避免了破壞一個共享對象。當生產者向soup里加入一個字母時,消費者不能吃字符,諸如此類。這種同步是維持共享對象完整性的重要方面。notify()函數將喚醒每一個等待線程。等待線程將繼續它的訪問。

  1.3.5聯繫起來

現在我們有一個生產者,一個消費者和一個共享對象,怎樣實現它們的交互呢?我們只需要一個簡單的控制程序來啟動所有的線程並確信每一個線程都是訪問的同一個共享對象。下面是控制程序的代碼,:

classSoupTest{publicstaticvoidmain(Stringargs[]){Soups=new

Soup();Producerp1=newProducer(s);Consumerc1=newConsumer(s);

t();t();}}

  1.3.6監視生產者

生產者/消費者模型程序經常用來實現遠程監視功能,它讓消費者看到生產者同用户的交互或同系統其它部分的交互。例如,在網絡中,一組生產者線程可以在很多工作站上運行。生產者可以打印文檔,文檔打印後,一個標誌將保存下來。一個(或多個?copy;消費者將保存標誌並在晚上報告白天打印活動的情況。另外,還有例子在一個工作站是分出幾個獨立的窗口。一個窗口用作用户輸入(生產者)另一個窗口作出對輸入的反應(消費者)。

  1.4線程API列表

下面是一個常用的線程類的方法函數列表:類函數:以下是Thread的靜態函數,即可以直接從Thread類調用。

currentThread返回正在運行的Thread對象yield停止運行當前線程,讓系統運行下一個線程sleep(intn)讓當前線程睡眠n毫秒對象函數:以下函數必須用Thread的實例對象來調用。

startstart函數告訴java運行系統為本線程建立一個執行環境,然後調用本線程的run()函數。run是運行本線程的將要執行的代碼,也是Runnable接口的唯一函數。當一個線程初始化後,由start函數來調用它,一?copy;run函數返回,本線程也就終止了。stop讓某線程馬上終止,系統將刪除本線程的執行環境suspend與stop函數不同,suspend將線程暫停執行,但系統不破壞線程的執行環境,你可以用resume來恢復本線程的執行resume恢復被掛起的線程進入運行狀態setPriority(intp)給線程設置優先級getPriority返回線程的優先級setName(Stringname)給線程設置名稱getName取線程的名稱