版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第16章多線程本章要點了解多線程在Windows系統(tǒng)的執(zhí)行模式掌握實現(xiàn)線程的兩種方式掌握線程的狀態(tài)掌握使線程進入各種狀態(tài)的方法掌握線程的優(yōu)先級掌握線程安全掌握線程同步機制掌握線程間的通信第16章多線程本章要點了解多線程在Windows系統(tǒng)的執(zhí)行第16章多線程1、線程簡介2、實現(xiàn)線程的兩種方法3、線程的生命周期4、操作線程的方法5、線程的優(yōu)先級6、線程同步7、線程間的通信主要內(nèi)容第16章多線程1、線程簡介主要內(nèi)容16.1線程簡介世間萬物會同時完成很多工作:例如人體同時進行呼吸、血液循環(huán)、思考問題等活動;用戶既可以使用計算機聽歌,也可以使用它打印文件,而這些活動完全可以同時進行,這種思想在Java中被稱為并發(fā),而將并發(fā)完成的每一件事情稱為線程。16.1線程簡介世間萬物會同時完成很多工作:16.1線程簡介在人們的生活中,并發(fā)機制非常重要,但是并不是所有的程序語言都支持線程。在以往的程序中,多以一個任務完成后再進行下一個項目的模式進行開發(fā),這樣下一個任務的開始必須等待前一個任務的結(jié)束。Java語言提供并發(fā)機制,程序員可以在程序中執(zhí)行多個線程,每一個線程完成一個功能,并與其他線程并發(fā)執(zhí)行,這種機制被稱為多線程。16.1線程簡介在人們的生活中,并發(fā)機制非常重要,但是并不16.1線程簡介Java中的多線程在每個操作系統(tǒng)中的運行方式也存在差異,在此著重說明多線程在Windows操作系統(tǒng)的運行模式。Windows操作系統(tǒng)是多任務操作系統(tǒng),它以進程為單位。一個進程是一個包含有自身地址的程序,每個獨立執(zhí)行的程序都稱為進程,也就是正在執(zhí)行的程序。16.1線程簡介Java中的多線程在每個操作系統(tǒng)中的運行方16.1線程簡介進程是一個用來描述處于動態(tài)運行狀態(tài)的應用程序的概念,即一個進程就是一個執(zhí)行中的程序,每個進程都有一塊自己獨立的地址空間,并可以包含多個線程。這些線程將共享進程的地址空間及操作系統(tǒng)分配給這個進程的資源。線程一般是指進程中的一個執(zhí)行流,多線程是指在一個進程中同時運行多個不同線程,每個線程分別執(zhí)行不同的任務。16.1線程簡介進程是一個用來描述處于動態(tài)運行狀態(tài)的應用程16.1線程簡介系統(tǒng)可以分配給每個進程一段有限的使用CPU的時間(也可以稱為CPU時間片),CPU在這段時間中執(zhí)行某個進程,然后下一個時間片又跳至另一個進程中去執(zhí)行。由于CPU轉(zhuǎn)換較快,所以使得每個進程好像是同時執(zhí)行一樣。16.1線程簡介系統(tǒng)可以分配給每個進程一段有限的使用CPU16.1線程簡介Windows操作系統(tǒng)的執(zhí)行模式16.1線程簡介Windows操作系統(tǒng)的執(zhí)行模式16.1線程簡介一個線程則是進程中的執(zhí)行流程,一個進程中可以同時包括多個線程,每個線程也可以得到一小段程序的執(zhí)行時間,這樣一個進程就可以具有多個并發(fā)執(zhí)行的線程。在單線程中,程序代碼按調(diào)用順序依次往下執(zhí)行,如果需要一個進程同時完成多段代碼的操作,就需要產(chǎn)生多線程。16.1線程簡介一個線程則是進程中的執(zhí)行流程,一個進程中可16.2實現(xiàn)線程的兩種方式16.2.1繼承Thread類16.2.2實現(xiàn)Runnable接口16.2實現(xiàn)線程的兩種方式16.2.1繼承Thread16.2.1繼承Thread類Thread類是java.lang包中的一個類,從這個類中實例化的對象代表線程,程序員啟動一個新線程需要建立Thread實例。Thread類中常用的兩個構(gòu)造方法如下:publicThread(StringthreadName);publicThread();其中第一個構(gòu)造方法是創(chuàng)建一個名稱為threadName的線程對象。16.2.1繼承Thread類Thread類是java.16.2.1繼承Thread類繼承Thread類創(chuàng)建一個新的線程的語法如下:publicclassThreadTestextendsThread{ //...}16.2.1繼承Thread類繼承Thread類創(chuàng)建一個新16.2.1繼承Thread類如果需要創(chuàng)建線程應該先定義一個Thread類的子類,并且覆蓋其中的run()成員方法,將線程執(zhí)行的程序代碼寫在其中。Thread對象需要一個任務來執(zhí)行,任務是指線程在啟動時執(zhí)行的工作,該工作的功能代碼被寫在run()方法中。16.2.1繼承Thread類如果需要創(chuàng)建線程應該先定義一16.2.1繼承Thread類這個run()方法必須使用如下這種語法格式:publicvoidrun(){ //...}注意:盡管在Thread的子類中覆蓋了run()成員方法,但用戶不能直接調(diào)用它,而是需要通過調(diào)用Thread類中的start()方法間接地使用它。16.2.1繼承Thread類這個run()方法必須使用如16.2.1繼承Thread類-注意1)start()方法的調(diào)用后并不是立即執(zhí)行多線程代碼,而是使得該線程變?yōu)榭蛇\行態(tài)(Runnable),什么時候運行是由操作系統(tǒng)決定的。2)如果start()方法調(diào)用一個已經(jīng)啟動的線程,系統(tǒng)將拋出IllegalThreadStateException異常。16.2.1繼承Thread類-注意1)start()方法16.2.1繼承Thread類-注意main()方法線程啟動由Java虛擬機負責,程序員負責啟動自己的線程。語法如下:publicstaticvoidmain(String[]args){ newThreadTest().start();}16.2.1繼承Thread類-注意main()方法線程啟publicclassMyThreadextendsThread{publicvoidrun(){for(intn=1;n<3;n++){for(inti=1;i<4;i++)System.out.print(getName()+"("+i+")");System.out.println();}System.out.println("exitfrom"+getName());}}創(chuàng)建兩個線程對象,分別實現(xiàn)重復顯示1~3數(shù)字的功能publicclassMyThreadextendspublicclassTest{publicstaticvoidmain(String[]args){Threadt1=newMyThread();t1.setName("T1");Threadt2=newMyThread();t2.setName("T2");t1.start();t2.start();System.out.println("exitfrom"+Thread.currentThread().getName());}}exitfrommainT2(1)T1(1)T1(2)T1(3)T2(2)T1(1)T1(2)T1(3)T2(3)exitfromT1T2(1)T2(2)T2(3)exitfromT2publicclassTest{exitfromm16.2.2實現(xiàn)Runnable接口到目前為止,線程都是通過擴展Thread類來創(chuàng)建的,如果程序員需要繼承其他類(非Thread類)并使該程序可以使用線程,就需要使用Runnable接口。實現(xiàn)Runnable接口的語法如下:publicclassMyThreadextendsObjectimplementsRunnable16.2.2實現(xiàn)Runnable接口到目前為止,線程都是通16.2.2實現(xiàn)Runnable接口實現(xiàn)Runnable接口的程序會創(chuàng)建一個Thread對象,并將Runnable對象與Thread對象相關(guān)聯(lián)。Thread類中有如下兩個構(gòu)造方法:publicThread(Runnabler)publicThread(Runnabler,Stringname)這兩個構(gòu)造方法的參數(shù)中都存在Runnable實例,使用以上構(gòu)造方法就可以將Runnable實例與Thread實例相關(guān)聯(lián)。16.2.2實現(xiàn)Runnable接口實現(xiàn)Runnable接16.2.2實現(xiàn)Runnable接口使用Runnable接口啟動新的線程的步驟如下:1)建立Runnable對象。2)使用參數(shù)為Runnable對象的構(gòu)造方法創(chuàng)建Thread實例。3)調(diào)用start()方法啟動線程。16.2.2實現(xiàn)Runnable接口使用Runnable接16.2.2實現(xiàn)Runnable接口16.2.2實現(xiàn)Runnable接口16.2.2實現(xiàn)Runnable接口通過Runnable接口創(chuàng)建線程時首先需要編寫一個實現(xiàn)Runnable接口的類,然后實例化該類的對象,這樣就建立了Runnable對象;接下來使用相應的構(gòu)造方法創(chuàng)建Thread實例;最后使用該實例調(diào)用Thread類中的Start()方法啟動線程。16.2.2實現(xiàn)Runnable接口通過Runnable接classMyThread2implementsRunnable{
privateStringname;
publicMyThread2(Stringname){
=name;}public
voidrun(){for(inti=1;i<10;i++)System.out.println(name+"運行:"+i);}}classMyThread2implementsRunpublic
static
voidmain(String[]args){
newThread(new
MyThread2("th1")).start();
newThread(new
MyThread2("th2")).start();/*MyThread2th1=newMyThread2("th1");Threadthobj1=newThread(th1);MyThread2th2=newMyThread2("th2");Threadthobj2=newThread(th2);thobj1.start();thobj2.start();*/}publicstaticvoidmain(String16.2.2實現(xiàn)Runnable接口實現(xiàn)Runnable接口比繼承Thread類所具有的優(yōu)勢:適合多個相同的程序代碼的線程去處理同一個資源可以避免java中的單繼承的限制增加程序的健壯性,代碼可以被多個線程共享,代碼和數(shù)據(jù)獨立16.2.2實現(xiàn)Runnable接口實現(xiàn)Runnable接16.3線程的生命周期線程生命周期中的各種狀態(tài)16.3線程的生命周期線程生命周期中的各種狀態(tài)16.3線程的生命周期線程具有生命周期,其中包含7種狀態(tài),分別為出生狀態(tài)、就緒狀態(tài)、運行狀態(tài)、等待狀態(tài)、休眠狀態(tài)、阻塞狀態(tài)和死亡狀態(tài)。出生狀態(tài)就是用戶在創(chuàng)建線程時處于的狀態(tài),在用戶使用該線程實例調(diào)用start()方法之前線程都處于出生狀態(tài);當用戶調(diào)用start()方法后,線程處于就緒狀態(tài)(又被稱為可執(zhí)行狀態(tài));當線程得到系統(tǒng)資源后就進入運行狀態(tài)。16.3線程的生命周期線程具有生命周期,其中包含7種狀態(tài),16.3線程的生命周期一旦線程進入可執(zhí)行狀態(tài),它會在就緒與運行狀態(tài)下輾轉(zhuǎn),同時也有可能進入等待、休眠、阻塞或死亡狀態(tài)。當處于運行狀態(tài)下的線程調(diào)用Thread類中的wait()方法,該線程處于等待狀態(tài),進入等待狀態(tài)的線程必須調(diào)用Thread類中的notify()方法才能被喚醒,而notifyAll()方法是將所有處于等待狀態(tài)下的線程喚醒;16.3線程的生命周期一旦線程進入可執(zhí)行狀態(tài),它會在就緒與16.3線程的生命周期當線程調(diào)用Thread類中的sleep()方法,則會進入休眠狀態(tài)。如果一個線程在運行狀態(tài)下發(fā)出輸入/輸出請求,該線程將進入阻塞狀態(tài),在其等待輸入/輸出結(jié)束時線程進入就緒狀態(tài),對于阻塞的線程來說,即使系統(tǒng)資源空閑,線程依然不能回到運行狀態(tài);當線程的run()方法執(zhí)行完畢時,線程進入死亡狀態(tài)。16.3線程的生命周期當線程調(diào)用Thread類中的slee16.3線程的生命周期雖然多線程看起來像同時執(zhí)行,但事實上在同一時間點上只有一個線程被執(zhí)行,只是線程之間切換較快,所以才會使人產(chǎn)生線程是同時進行的假象。在Windows操作系統(tǒng)中,系統(tǒng)會為每個線程分配一小段CPU時間片,一旦CPU時間片結(jié)束就會將當前線程換為下一個線程,即使該線程沒有結(jié)束的情況下。16.3線程的生命周期雖然多線程看起來像同時執(zhí)行,但事實上16.3線程的生命周期根據(jù)圖16-5所示,可以總結(jié)出使線程進入阻塞狀態(tài)有以下幾種可能。調(diào)用sleep()方法。調(diào)用wait()方法。等待輸入/輸出完成。16.3線程的生命周期根據(jù)圖16-5所示,可以總結(jié)出使線程16.3線程的生命周期當線程處于阻塞狀態(tài)后,可通過以下幾種方式使線程再次進入就緒狀態(tài)。線程調(diào)用notify()方法。線程調(diào)用notifyAll()方法。線程調(diào)用interrupt()方法。線程的休眠時間結(jié)束。
輸入/輸出結(jié)束。16.3線程的生命周期當線程處于阻塞狀態(tài)后,可通過以下幾種16.4操作線程的方法16.4.1線程的休眠16.4.2線程的加入16.4.3線程的中斷16.4.4線程的禮讓16.4操作線程的方法16.4.1線程的休眠16.4.1線程的休眠一種能控制線程行為的方法是調(diào)用sleep()方法,sleep()方法需要一個參數(shù)用于指定該線程休眠的時間,該時間使用毫秒為單位。它通常是在run()方法內(nèi)的循環(huán)中被使用。sleep()方法的語法如下:try{ Thread.sleep(2000);}catch(InterruptedExceptione){ e.printStackTrace();}16.4.1線程的休眠一種能控制線程行為的方法是調(diào)用slepublic
voidrun(){System.out.println("開始執(zhí)行線程。。。");System.out.println("進入睡眠狀態(tài)。。。");
try{Thread.sleep(3000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("線程結(jié)束。。。");}publicvoidrun(){16.4.2線程的加入如果當前某程序為多線程程序,假如存在一個線程A,現(xiàn)在需要插入線程B,并要求線程B先執(zhí)行完畢,然后再繼續(xù)執(zhí)行線程A,此時可以使用Thread類中的join()方法來完成。這就好比此時正在看電視,卻突然有人上門收水費,必須付完水費后才能繼續(xù)看電視。當某個線程使用join()方法加入到另外一個線程時,另一個線程會等待該線程執(zhí)行完畢再繼續(xù)執(zhí)行。16.4.2線程的加入如果當前某程序為多線程程序,假如存在16.4.2線程的加入為什么要用join()方法在很多情況下,主線程生成并起動了子線程,如果子線程里要進行大量的耗時的運算,主線程往往將于子線程之前結(jié)束,但是如果主線程處理完其他的事務后,需要用到子線程的處理結(jié)果,也就是主線程需要等待子線程執(zhí)行完成之后再結(jié)束,這個時候就要用到join()方法了。16.4.2線程的加入為什么要用join()方法classThread1extendsThread{privateStringname;
publicThread1(Stringname){=name;}
public
voidrun(){
for(inti=0;i<3;i++){System.out.println("子線程"+name+"運行:"+i);
try{
sleep((int)Math.random()*10);}catch(InterruptedExceptione){e.printStackTrace();}
}
}
}classThread1extendsThread{public
static
voidmain(String[]args){
System.out.println(Thread.currentThread().getName()+"主線程運行開始!");
Thread1mTh1=newThread1("A");Thread1mTh2=newThread1("B");mTh1.start();mTh2.start();
System.out.println(Thread.currentThread().getName()+"主線程運行結(jié)束!");
}main主線程運行開始!main主線程運行結(jié)束!子線程B運行:0子線程A運行:0子線程B運行:1子線程A運行:1子線程B運行:2子線程A運行:2publicstaticvoidmain(Stringpublic
static
voidmain(String[]args){…
Thread1mTh1=newThread1("A");Thread1mTh2=newThread1("B");mTh1.start();mTh2.start();
try{mTh1.join();mTh2.join();}catch(InterruptedExceptione){e.printStackTrace();}…}main主線程運行開始!子線程B運行:0子線程B運行:1子線程B運行:2子線程A運行:0子線程A運行:1子線程A運行:2main主線程運行結(jié)束!publicstaticvoidmain(String16.4.3線程的中斷以前使用stop()方法停止線程,但當前版本的JDK早已廢除了stop()方法,同時也不建議使用stop()方法來停止一個線程的運行?,F(xiàn)在提倡在run()方法中使用無限循環(huán)的形式,然后使用一個布爾型標記控制循環(huán)的停止。16.4.3線程的中斷以前使用stop()方法停止線程,但classThreadTestextendsThread{
private
intcount=10;
public
voidrun(){
while(true){System.out.print(count+"");
if(--count==0){
return;}}}}classThreadTestextendsThrea16.4.4線程的禮讓Thread類中使用yield()方法表示禮讓,它只是給當前正處于運行狀態(tài)下的線程一個提醒,告知它可以將資源禮讓給其他線程。但這僅僅是一種暗示,沒有任何一種機制保證當前線程會將資源禮讓。對于支持多任務的操作系統(tǒng)來說,不需要調(diào)用yeild()方法,因為操作系統(tǒng)會為線程自動分配CPU時間片來執(zhí)行。16.4.4線程的禮讓Thread類中使用yield()方16.5線程的優(yōu)先級每個線程都具有各自的優(yōu)先級,線程的優(yōu)先級可以在程序中表明該線程的重要性,如果有很多線程處于就緒狀態(tài),系統(tǒng)會根據(jù)優(yōu)先級來決定首先使哪個線程進入運行狀態(tài)。但這并不意味著低優(yōu)先級的線程得不到運行,而只是它運行的幾率比較小,比如垃圾回收線程的優(yōu)先級就較低。16.5線程的優(yōu)先級每個線程都具有各自的優(yōu)先級,線程的優(yōu)先16.5線程的優(yōu)先級Thread類中包含的靜態(tài)成員變量代表了線程的某些優(yōu)先級,比如Thread.MIN_PRIORITY(常數(shù)1)、Thread.MAX_PRIORITY(常數(shù)10)、Thread.NORM_PRIORITY(常數(shù)5)。其中每個線程的優(yōu)先級都在Thread.MIN_PRIORITY~Thread.MAX_PRIORITY之間,在默認情況下其優(yōu)先級都是Thread.NORM_PRIORITY。每個新產(chǎn)生的線程都繼承了父線程的優(yōu)先級。16.5線程的優(yōu)先級Thread類中包含的靜態(tài)成員變量代表16.5線程的優(yōu)先級在多任務操作系統(tǒng)中,每個線程都會得到一小段CPU時間片運行,在時間結(jié)束時,將輪換另一個線程進入運行狀態(tài),這時系統(tǒng)會選擇與當前線程優(yōu)先級相同的線程予以運行。系統(tǒng)始終選擇就緒狀態(tài)下優(yōu)先級較高的線程進入運行狀態(tài)。線程的優(yōu)先級可以使用setPriority()方法調(diào)整,如果使用該方法設置的優(yōu)先級不在1~10之內(nèi),將產(chǎn)生一個IllegalArgumentException異常。16.5線程的優(yōu)先級在多任務操作系統(tǒng)中,每個線程都會得到一16.5線程的優(yōu)先級16.5線程的優(yōu)先級16.5線程的優(yōu)先級在圖16-9中,優(yōu)先級為5的線程A首先得到CPU時間片;當該時間結(jié)束后,輪換到與線程A相同優(yōu)先級的線程B;當線程B的運行時間結(jié)束后,會繼續(xù)輪換到線程A,直到線程A與線程B都執(zhí)行完畢,才會輪換到線程C;當線程C結(jié)束后,最后才會輪到線程D。16.5線程的優(yōu)先級在圖16-9中,優(yōu)先級為5的線程A首先classMyThread51extendsThread{
publicMyThread51(Stringname){
super(name);}
public
voidrun(){
for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+"("+Thread.currentThread().getPriority()+")"+",loop"+i);}}
}classMyThread51extendsThrea
public
static
voidmain(String[]args){
System.out.println(Thread.currentThread().getName()+"("+Thread.currentThread().getPriority()+")");Threadt1=newMyThread51("t1");Threadt2=newMyThread51("t2");t1.setPriority(1);t2.setPriority(10);
t1.start();t2.start();
}main(5)t1(1),loop0t1(1),loop1t1(1),loop2t1(1),loop3t1(1),loop4t2(10),loop0t2(10),loop1t2(10),loop2t2(10),loop3t2(10),loop4publicstaticvoidmain(Strin16.6線程同步16.6.1線程安全16.6.2線程同步機制16.6線程同步16.6.1線程安全16.6.1線程安全所以在編寫多線程程序時,應該考慮到線程安全問題。實質(zhì)上線程安全問題來源于兩個線程同時存取單一對象的數(shù)據(jù)。在一個時刻只能被一個線程訪問的資源稱為臨界資源,而訪問臨界資源的那段代碼被稱為臨界區(qū)。臨界區(qū)的使用必須互斥地進行,即一個線程在臨界區(qū)中執(zhí)行代碼時,其他線程不能進入臨界區(qū)。16.6.1線程安全所以在編寫多線程程序時,應該考慮到線程16.6.1線程安全在代碼中判斷當前票數(shù)是否大于0,如果大于0則執(zhí)行將該票出售給乘客功能,但當兩個線程同時訪問這段代碼時(假如這時只剩下一張票),第一個線程將票售出,與此同時第二個線程也已經(jīng)執(zhí)行完成判斷是否有票的操作,并得出結(jié)論票數(shù)大于0,于是它也執(zhí)行售出操作,這樣就會產(chǎn)生負數(shù)。16.6.1線程安全在代碼中判斷當前票數(shù)是否大于0,如果大classThreadSafeTestimplementsRunnable{
intnum=10;//設置當前總票數(shù)
public
voidrun(){
while(num>0){
try{Thread.sleep(100);}catch(Exceptione){e.printStackTrace();}System.out.println("tickets"+num--);}}}classThreadSafeTestimplement
public
static
voidmain(String[]args){ThreadSafeTestt=newThreadSafeTest();ThreadtA=newThread(t);
//以該類對象分別實例化4個線程
ThreadtB=newThread(t);ThreadtC=newThread(t);ThreadtD=newThread(t);tA.start();//分別啟動線程
tB.start();tC.start();tD.start();}tickets10tickets8tickets9tickets7tickets6tickets4tickets5tickets6tickets3tickets2tickets1tickets0tickets-1tickets-2publicstaticvoidmain(Strin16.6.2線程同步機制如何解決資源共享的問題?基本上所有解決多線程資源沖突問題都會采用給定時間只允許一個線程訪問共享資源,這時就需要給共享資源上一道鎖。為了解決多線程并發(fā)操作可能引起的數(shù)據(jù)混亂,在java中,引入對象“互斥鎖”,以保證共享數(shù)據(jù)操作的完整性。16.6.2線程同步機制如何解決資源共享的問題?16.6.2線程同步機制1)同步塊在Java中提供了同步機制,可以有效地防止資源沖突。同步機制使用synchronized關(guān)鍵字。
synchronized(Object){}16.6.2線程同步機制1)同步塊Object為任意一個對象,每個對象都存在一個標志位,并具有兩個值,分別為0和1。一個線程運行到同步塊時首先檢查該對象的標志位,如果為0狀態(tài),表明此同步塊中存在其他線程在運行。這時該線程就處于就緒狀態(tài),直到處于同步塊中的線程執(zhí)行完同步塊中的代碼為止。這時該對象的標識位被設置為1,該線程才能執(zhí)行同步塊中的代碼,并將Object對象的標識位設置為0,防止其他線程執(zhí)行同步塊中的代碼。Object為任意一個對象,每個對象都存在一個標志位,并具有
public
voidrun(){
synchronized(""){
while(num>0){
try{Thread.sleep(100);}catch(Exceptione){e.printStackTrace();}System.out.println("tickets"+num--);}
}}publicvoidrun(){16.6.2線程同步機制2)同步方法同步方法就是在方法前面修飾synchronized關(guān)鍵字的方法,其語法如下。
synchronizedvoidf(){}當某個對象調(diào)用了同步方法,該對象上的其他同步方法必須等待該同步方法執(zhí)行完畢才能被執(zhí)行。必須將每個能訪問共享資源的方法修飾為synchronized,否則就會出錯。16.6.2線程同步機制2)同步方法16.6.2線程同步機制無論synchronized關(guān)鍵字加在方法上還是對象上,它取得的鎖都是對象,而不是把一段代碼或函數(shù)當作鎖。每個對象只有一個鎖(lock)與之相關(guān)聯(lián)。實現(xiàn)同步是要很大的系統(tǒng)開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制。16.6.2線程同步機制無論synchronized關(guān)鍵字16.7線程間的通信線程之間的通信使用wait()、notify()以及notifyAll()方法實現(xiàn)。線程調(diào)用wait()方法后可以使該線程從運行狀態(tài)進入就緒狀態(tài),sleep()方法也達到這樣一個效果,那么兩者究竟有何區(qū)別?從同步的角度上來說,調(diào)用sleep()方法的線程不釋放鎖,但調(diào)用wait()方法的線程釋放鎖。16.7線程間的通信線程之間的通信使用wait()、not16.7線程間的通信使用wait()方法有以下兩種形式:wait(time)wait()第一種形式的wait()方法與sleep()方法的含義相同,都是指在此時間之內(nèi)暫停;而第二種形式的wait()方法會使線程永久無限地等待下去,需要使用notify()或者notifyAll()方法喚醒。16.7線程間的通信使用wait()方法有以下兩種形式:16.7線程間的通信notify()方法用來喚醒一個處于阻塞狀態(tài)的線程,任何一個已經(jīng)滿足了被喚醒條件的線程都可能被喚醒。notifyAll()方法可以喚醒所有處于等待狀態(tài)的線程。16.7線程間的通信notify()方法用來喚醒一個處于阻
wait和sleep區(qū)別共同點:1.他們都是在多線程的環(huán)境下,都可以在程序的調(diào)用處阻塞指定的毫秒數(shù),并返回。2.wait()和sleep()都可以通過interrupt()方法打斷線程的暫停狀態(tài),從而使線程立刻拋出InterruptedException。
wait和sleep區(qū)別共同點:
wait和sleep區(qū)別如果線程A希望立即結(jié)束線程B,則可以對線程B對應的Thread實例調(diào)用interrupt方法。如果此刻線程B正在wait/sleep/join,則線程B會立刻拋出InterruptedException,在catch(){}中直接return即可安全地結(jié)束線程。
wait和sleep區(qū)別如果線程A希望立即結(jié)束線程B,則可
wait和sleep區(qū)別不同點:1.Thread類的方法:sleep(),yield()等
Object的方法:wait()和notify()等2.每個對象都有一個鎖來控制同步訪問。Synchronized關(guān)鍵字可以和對象的鎖交互,來實現(xiàn)線程的同步。
sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。
wait和sleep區(qū)別不同點:
wait和sleep區(qū)別不同點:3.wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用4.sleep必須捕獲異常,而wait,notify和notifyAll不需要捕獲異常
wait和sleep區(qū)別不同點:第16章多線程本章要點了解多線程在Windows系統(tǒng)的執(zhí)行模式掌握實現(xiàn)線程的兩種方式掌握線程的狀態(tài)掌握使線程進入各種狀態(tài)的方法掌握線程的優(yōu)先級掌握線程安全掌握線程同步機制掌握線程間的通信第16章多線程本章要點了解多線程在Windows系統(tǒng)的執(zhí)行第16章多線程1、線程簡介2、實現(xiàn)線程的兩種方法3、線程的生命周期4、操作線程的方法5、線程的優(yōu)先級6、線程同步7、線程間的通信主要內(nèi)容第16章多線程1、線程簡介主要內(nèi)容16.1線程簡介世間萬物會同時完成很多工作:例如人體同時進行呼吸、血液循環(huán)、思考問題等活動;用戶既可以使用計算機聽歌,也可以使用它打印文件,而這些活動完全可以同時進行,這種思想在Java中被稱為并發(fā),而將并發(fā)完成的每一件事情稱為線程。16.1線程簡介世間萬物會同時完成很多工作:16.1線程簡介在人們的生活中,并發(fā)機制非常重要,但是并不是所有的程序語言都支持線程。在以往的程序中,多以一個任務完成后再進行下一個項目的模式進行開發(fā),這樣下一個任務的開始必須等待前一個任務的結(jié)束。Java語言提供并發(fā)機制,程序員可以在程序中執(zhí)行多個線程,每一個線程完成一個功能,并與其他線程并發(fā)執(zhí)行,這種機制被稱為多線程。16.1線程簡介在人們的生活中,并發(fā)機制非常重要,但是并不16.1線程簡介Java中的多線程在每個操作系統(tǒng)中的運行方式也存在差異,在此著重說明多線程在Windows操作系統(tǒng)的運行模式。Windows操作系統(tǒng)是多任務操作系統(tǒng),它以進程為單位。一個進程是一個包含有自身地址的程序,每個獨立執(zhí)行的程序都稱為進程,也就是正在執(zhí)行的程序。16.1線程簡介Java中的多線程在每個操作系統(tǒng)中的運行方16.1線程簡介進程是一個用來描述處于動態(tài)運行狀態(tài)的應用程序的概念,即一個進程就是一個執(zhí)行中的程序,每個進程都有一塊自己獨立的地址空間,并可以包含多個線程。這些線程將共享進程的地址空間及操作系統(tǒng)分配給這個進程的資源。線程一般是指進程中的一個執(zhí)行流,多線程是指在一個進程中同時運行多個不同線程,每個線程分別執(zhí)行不同的任務。16.1線程簡介進程是一個用來描述處于動態(tài)運行狀態(tài)的應用程16.1線程簡介系統(tǒng)可以分配給每個進程一段有限的使用CPU的時間(也可以稱為CPU時間片),CPU在這段時間中執(zhí)行某個進程,然后下一個時間片又跳至另一個進程中去執(zhí)行。由于CPU轉(zhuǎn)換較快,所以使得每個進程好像是同時執(zhí)行一樣。16.1線程簡介系統(tǒng)可以分配給每個進程一段有限的使用CPU16.1線程簡介Windows操作系統(tǒng)的執(zhí)行模式16.1線程簡介Windows操作系統(tǒng)的執(zhí)行模式16.1線程簡介一個線程則是進程中的執(zhí)行流程,一個進程中可以同時包括多個線程,每個線程也可以得到一小段程序的執(zhí)行時間,這樣一個進程就可以具有多個并發(fā)執(zhí)行的線程。在單線程中,程序代碼按調(diào)用順序依次往下執(zhí)行,如果需要一個進程同時完成多段代碼的操作,就需要產(chǎn)生多線程。16.1線程簡介一個線程則是進程中的執(zhí)行流程,一個進程中可16.2實現(xiàn)線程的兩種方式16.2.1繼承Thread類16.2.2實現(xiàn)Runnable接口16.2實現(xiàn)線程的兩種方式16.2.1繼承Thread16.2.1繼承Thread類Thread類是java.lang包中的一個類,從這個類中實例化的對象代表線程,程序員啟動一個新線程需要建立Thread實例。Thread類中常用的兩個構(gòu)造方法如下:publicThread(StringthreadName);publicThread();其中第一個構(gòu)造方法是創(chuàng)建一個名稱為threadName的線程對象。16.2.1繼承Thread類Thread類是java.16.2.1繼承Thread類繼承Thread類創(chuàng)建一個新的線程的語法如下:publicclassThreadTestextendsThread{ //...}16.2.1繼承Thread類繼承Thread類創(chuàng)建一個新16.2.1繼承Thread類如果需要創(chuàng)建線程應該先定義一個Thread類的子類,并且覆蓋其中的run()成員方法,將線程執(zhí)行的程序代碼寫在其中。Thread對象需要一個任務來執(zhí)行,任務是指線程在啟動時執(zhí)行的工作,該工作的功能代碼被寫在run()方法中。16.2.1繼承Thread類如果需要創(chuàng)建線程應該先定義一16.2.1繼承Thread類這個run()方法必須使用如下這種語法格式:publicvoidrun(){ //...}注意:盡管在Thread的子類中覆蓋了run()成員方法,但用戶不能直接調(diào)用它,而是需要通過調(diào)用Thread類中的start()方法間接地使用它。16.2.1繼承Thread類這個run()方法必須使用如16.2.1繼承Thread類-注意1)start()方法的調(diào)用后并不是立即執(zhí)行多線程代碼,而是使得該線程變?yōu)榭蛇\行態(tài)(Runnable),什么時候運行是由操作系統(tǒng)決定的。2)如果start()方法調(diào)用一個已經(jīng)啟動的線程,系統(tǒng)將拋出IllegalThreadStateException異常。16.2.1繼承Thread類-注意1)start()方法16.2.1繼承Thread類-注意main()方法線程啟動由Java虛擬機負責,程序員負責啟動自己的線程。語法如下:publicstaticvoidmain(String[]args){ newThreadTest().start();}16.2.1繼承Thread類-注意main()方法線程啟publicclassMyThreadextendsThread{publicvoidrun(){for(intn=1;n<3;n++){for(inti=1;i<4;i++)System.out.print(getName()+"("+i+")");System.out.println();}System.out.println("exitfrom"+getName());}}創(chuàng)建兩個線程對象,分別實現(xiàn)重復顯示1~3數(shù)字的功能publicclassMyThreadextendspublicclassTest{publicstaticvoidmain(String[]args){Threadt1=newMyThread();t1.setName("T1");Threadt2=newMyThread();t2.setName("T2");t1.start();t2.start();System.out.println("exitfrom"+Thread.currentThread().getName());}}exitfrommainT2(1)T1(1)T1(2)T1(3)T2(2)T1(1)T1(2)T1(3)T2(3)exitfromT1T2(1)T2(2)T2(3)exitfromT2publicclassTest{exitfromm16.2.2實現(xiàn)Runnable接口到目前為止,線程都是通過擴展Thread類來創(chuàng)建的,如果程序員需要繼承其他類(非Thread類)并使該程序可以使用線程,就需要使用Runnable接口。實現(xiàn)Runnable接口的語法如下:publicclassMyThreadextendsObjectimplementsRunnable16.2.2實現(xiàn)Runnable接口到目前為止,線程都是通16.2.2實現(xiàn)Runnable接口實現(xiàn)Runnable接口的程序會創(chuàng)建一個Thread對象,并將Runnable對象與Thread對象相關(guān)聯(lián)。Thread類中有如下兩個構(gòu)造方法:publicThread(Runnabler)publicThread(Runnabler,Stringname)這兩個構(gòu)造方法的參數(shù)中都存在Runnable實例,使用以上構(gòu)造方法就可以將Runnable實例與Thread實例相關(guān)聯(lián)。16.2.2實現(xiàn)Runnable接口實現(xiàn)Runnable接16.2.2實現(xiàn)Runnable接口使用Runnable接口啟動新的線程的步驟如下:1)建立Runnable對象。2)使用參數(shù)為Runnable對象的構(gòu)造方法創(chuàng)建Thread實例。3)調(diào)用start()方法啟動線程。16.2.2實現(xiàn)Runnable接口使用Runnable接16.2.2實現(xiàn)Runnable接口16.2.2實現(xiàn)Runnable接口16.2.2實現(xiàn)Runnable接口通過Runnable接口創(chuàng)建線程時首先需要編寫一個實現(xiàn)Runnable接口的類,然后實例化該類的對象,這樣就建立了Runnable對象;接下來使用相應的構(gòu)造方法創(chuàng)建Thread實例;最后使用該實例調(diào)用Thread類中的Start()方法啟動線程。16.2.2實現(xiàn)Runnable接口通過Runnable接classMyThread2implementsRunnable{
privateStringname;
publicMyThread2(Stringname){
=name;}public
voidrun(){for(inti=1;i<10;i++)System.out.println(name+"運行:"+i);}}classMyThread2implementsRunpublic
static
voidmain(String[]args){
newThread(new
MyThread2("th1")).start();
newThread(new
MyThread2("th2")).start();/*MyThread2th1=newMyThread2("th1");Threadthobj1=newThread(th1);MyThread2th2=newMyThread2("th2");Threadthobj2=newThread(th2);thobj1.start();thobj2.start();*/}publicstaticvoidmain(String16.2.2實現(xiàn)Runnable接口實現(xiàn)Runnable接口比繼承Thread類所具有的優(yōu)勢:適合多個相同的程序代碼的線程去處理同一個資源可以避免java中的單繼承的限制增加程序的健壯性,代碼可以被多個線程共享,代碼和數(shù)據(jù)獨立16.2.2實現(xiàn)Runnable接口實現(xiàn)Runnable接16.3線程的生命周期線程生命周期中的各種狀態(tài)16.3線程的生命周期線程生命周期中的各種狀態(tài)16.3線程的生命周期線程具有生命周期,其中包含7種狀態(tài),分別為出生狀態(tài)、就緒狀態(tài)、運行狀態(tài)、等待狀態(tài)、休眠狀態(tài)、阻塞狀態(tài)和死亡狀態(tài)。出生狀態(tài)就是用戶在創(chuàng)建線程時處于的狀態(tài),在用戶使用該線程實例調(diào)用start()方法之前線程都處于出生狀態(tài);當用戶調(diào)用start()方法后,線程處于就緒狀態(tài)(又被稱為可執(zhí)行狀態(tài));當線程得到系統(tǒng)資源后就進入運行狀態(tài)。16.3線程的生命周期線程具有生命周期,其中包含7種狀態(tài),16.3線程的生命周期一旦線程進入可執(zhí)行狀態(tài),它會在就緒與運行狀態(tài)下輾轉(zhuǎn),同時也有可能進入等待、休眠、阻塞或死亡狀態(tài)。當處于運行狀態(tài)下的線程調(diào)用Thread類中的wait()方法,該線程處于等待狀態(tài),進入等待狀態(tài)的線程必須調(diào)用Thread類中的notify()方法才能被喚醒,而notifyAll()方法是將所有處于等待狀態(tài)下的線程喚醒;16.3線程的生命周期一旦線程進入可執(zhí)行狀態(tài),它會在就緒與16.3線程的生命周期當線程調(diào)用Thread類中的sleep()方法,則會進入休眠狀態(tài)。如果一個線程在運行狀態(tài)下發(fā)出輸入/輸出請求,該線程將進入阻塞狀態(tài),在其等待輸入/輸出結(jié)束時線程進入就緒狀態(tài),對于阻塞的線程來說,即使系統(tǒng)資源空閑,線程依然不能回到運行狀態(tài);當線程的run()方法執(zhí)行完畢時,線程進入死亡狀態(tài)。16.3線程的生命周期當線程調(diào)用Thread類中的slee16.3線程的生命周期雖然多線程看起來像同時執(zhí)行,但事實上在同一時間點上只有一個線程被執(zhí)行,只是線程之間切換較快,所以才會使人產(chǎn)生線程是同時進行的假象。在Windows操作系統(tǒng)中,系統(tǒng)會為每個線程分配一小段CPU時間片,一旦CPU時間片結(jié)束就會將當前線程換為下一個線程,即使該線程沒有結(jié)束的情況下。16.3線程的生命周期雖然多線程看起來像同時執(zhí)行,但事實上16.3線程的生命周期根據(jù)圖16-5所示,可以總結(jié)出使線程進入阻塞狀態(tài)有以下幾種可能。調(diào)用sleep()方法。調(diào)用wait()方法。等待輸入/輸出完成。16.3線程的生命周期根據(jù)圖16-5所示,可以總結(jié)出使線程16.3線程的生命周期當線程處于阻塞狀態(tài)后,可通過以下幾種方式使線程再次進入就緒狀態(tài)。線程調(diào)用notify()方法。線程調(diào)用notifyAll()方法。線程調(diào)用interrupt()方法。線程的休眠時間結(jié)束。
輸入/輸出結(jié)束。16.3線程的生命周期當線程處于阻塞狀態(tài)后,可通過以下幾種16.4操作線程的方法16.4.1線程的休眠16.4.2線程的加入16.4.3線程的中斷16.4.4線程的禮讓16.4操作線程的方法16.4.1線程的休眠16.4.1線程的休眠一種能控制線程行為的方法是調(diào)用sleep()方法,sleep()方法需要一個參數(shù)用于指定該線程休眠的時間,該時間使用毫秒為單位。它通常是在run()方法內(nèi)的循環(huán)中被使用。sleep()方法的語法如下:try{ Thread.sleep(2000);}catch(InterruptedExceptione){ e.printStackTrace();}16.4.1線程的休眠一種能控制線程行為的方法是調(diào)用slepublic
voidrun(){System.out.println("開始執(zhí)行線程。。。");System.out.println("進入睡眠狀態(tài)。。。");
try{Thread.sleep(3000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("線程結(jié)束。。。");}publicvoidrun(){16.4.2線程的加入如果當前某程序為多線程程序,假如存在一個線程A,現(xiàn)在需要插入線程B,并要求線程B先執(zhí)行完畢,然后再繼續(xù)執(zhí)行線程A,此時可以使用Thread類中的join()方法來完成。這就好比此時正在看電視,卻突然有人上門收水費,必須付完水費后才能繼續(xù)看電視。當某個線程使用join()方法加入到另外一個線程時,另一個線程會等待該線程執(zhí)行完畢再繼續(xù)執(zhí)行。16.4.2線程的加入如果當前某程序為多線程程序,假如存在16.4.2線程的加入為什么要用join()方法在很多情況下,主線程生成并起動了子線程,如果子線程里要進行大量的耗時的運算,主線程往往將于子線程之前結(jié)束,但是如果主線程處理完其他的事務后,需要用到子線程的處理結(jié)果,也就是主線程需要等待子線程執(zhí)行完成之后再結(jié)束,這個時候就要用到join()方法了。16.4.2線程的加入為什么要用join()方法classThread1extendsThread{privateStringname;
publicThread1(Stringname){=name;}
public
voidrun(){
for(inti=0;i<3;i++){System.out.println("子線程"+name+"運行:"+i);
try{
sleep((int)Math.random()*10);}catch(InterruptedExceptione){e.printStackTrace();}
}
}
}classThread1extendsThread{public
static
voidmain(String[]args){
System.out.println(Thread.currentThread().getName()+"主線程運行開始!");
Thread1mTh1=newThread1("A");Thread1mTh2=newThread1("B");mTh1.start();mTh2.start();
System.out.println(Thread.currentThread().getName()+"主線程運行結(jié)束!");
}main主線程運行開始!main主線程運行結(jié)束!子線程B運行:0子線程A運行:0子線程B運行:1子線程A運行:1子線程B運行:2子線程A運行:2publicstaticvoidmain(Stringpublic
static
voidmain(String[]args){…
Thread1mTh1=newThread1("A");Thread1mTh2=newThread1("B");mTh1.start();mTh2.start();
try{mTh1.join();mTh2.join();}catch(InterruptedExceptione){e.printStackTrace();}…}main主線程運行開始!子線程B運行:0子線程B運行:1子線程B運行:2子線程A運行:0子線程A運行:1子線程A運行:2main主線程運行結(jié)束!publicstaticvoidmain(String16.4.3線程的中斷以前使用stop()方法停止線程,但當前版本的JDK早已廢除了stop()方法,同時也不建議使用stop()方法來停止一個線程的運行?,F(xiàn)在提倡在run()方法中使用無限循環(huán)的形式,然后使用一個布爾型標記控制循環(huán)的停止。16.4.3線程的中斷以前使用stop()方法停止線程,但classThreadTestextendsThread{
private
intcount=10;
public
voidrun(){
while(true){System.out.print(count+"");
if(--count==0){
return;}}}}classThreadTestextendsThrea16.4.4線程的禮讓Thread類中使用yield()方法表示禮讓,它只是給當前正處于運行狀態(tài)下的線程一個提醒,告知它可以將資源禮讓給其他線程。但這僅僅是一種暗示,沒有任何一種機制保證當前線程會將資源禮讓。對于支持多任務的操作系統(tǒng)來說,不需要調(diào)用yeild()方法,因為操作系統(tǒng)會為線程自動分配CPU時間片來執(zhí)行。16.4.4線程的禮讓Thread類中使用yield()方16.5線程的優(yōu)先級每個線程都具有各自的優(yōu)先級,線程的優(yōu)先級可以在程序中表明該線程的重要性,如果有很多線程處于就緒狀態(tài),系統(tǒng)會根據(jù)優(yōu)先級來決定首先使哪個線程進入運行狀態(tài)。但這并不意味著低優(yōu)先級的線程得不到運行,而只是它運行的幾率比較小,比如垃圾回收線程的優(yōu)先級就較低。16.5線程的優(yōu)先級每個線程都具有各自的優(yōu)先級,線程的優(yōu)先16.5線程的優(yōu)先級Thread類中包含的靜態(tài)成員變量代表了線程的某些優(yōu)先級,比如Thread.MIN_PRIORITY(常數(shù)1)、Thread.MAX_PRIORITY(常數(shù)10)、Thread.NORM_PRIORITY(常數(shù)5)。其中每個線程的優(yōu)先級都在Thread.MIN_PRIORITY~Thread.MAX_PRIORITY之間,在默認情況下其優(yōu)先級都是Thread.NORM_PRIORITY。每個新產(chǎn)生的線程都繼承了父線程的優(yōu)先級。16.5線程的優(yōu)先級Thread類中包含的靜態(tài)成員變量代表16.5線程的優(yōu)先級在多任務操作系統(tǒng)中,每個線程都會得到一小段CPU時間片運行,在時間結(jié)束時,將輪換另一個線程進入運行狀態(tài),這時系統(tǒng)會選擇與當前線程優(yōu)先級相同的線程予以運行。系統(tǒng)始終選擇就緒狀態(tài)下優(yōu)先級較高的線程進入運行狀態(tài)。線程的優(yōu)先級可以使用setPriority()方法調(diào)整,如果使用該方法設置的優(yōu)先級不在1~10之內(nèi),將產(chǎn)生一個IllegalArgumentException異常。16.5線程的優(yōu)先級在多任務操作系統(tǒng)中,每個線程都會得到一16.5線程的優(yōu)先級16.5線程的優(yōu)先級16.5線程的優(yōu)先級在圖16-9中,優(yōu)先級為5的線程A首先得到CPU時間片;當該時間結(jié)束后,輪換到與線程A相同優(yōu)先級的線程B;當線程B的運行時間結(jié)束后,會繼續(xù)輪換到線程A,直到線程A與線程B都執(zhí)行完畢,才會輪換到線程C;當線程C結(jié)束后,最后才會輪到線程D。16.5線程的優(yōu)先級在圖16-9中,優(yōu)先級為5的線程A首先classMyThread51extendsThread{
publicMyThread51(Stringname){
super(name);}
public
voidrun(){
for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+"("+Thread.currentThread().getPriority()+")"+",loop"+i);}}
}classMyThread51extendsThrea
public
static
voidmain(String[]args){
System.out.println(Thread.currentThread().getName()+"("+Thread.currentThread().getPriority()+")");Threadt1=newMyThread51("t1");Threadt2=newMyThread51("t2");t1.setPriority(1);t2.setPriority(10);
t1.start();t2.start();
}main(5)t1(1),loop0t1(1),loop1t1(1),loop2t1(1),loop3t1(1),loop4t2(10),loop0t2(10),loop1t2(10),loop2t2(10),loop3t2(10),loop4publicstaticvoidmain(Strin16.6線程同步16.6.1線程安全16.6.2線程同步機制16.6線程同步16.6.1線程安全16.6.1線程安全所以在編寫多線程程序時,應該考慮到線程安全問題。實質(zhì)上線程安全問題來源于兩個線程同時存取單一對象的數(shù)據(jù)。在一個時刻只能被一個線程訪問的資源稱為臨界資源,而訪問臨界資源的那段代碼被稱為臨界區(qū)。臨界區(qū)的使用必須互斥地進行,即一個線程在臨界區(qū)中執(zhí)行代碼時,其他線程不能進入臨界區(qū)。16.6.1線程安全所以在編寫多線程程序時,應該考慮到線程16.6.1線程安全在代碼中判斷當前票數(shù)是否大于0,如果大于0則執(zhí)行將該票出售給乘客功能,但當兩個線程同時訪問這段代碼時(假如這時只剩下一張票),第一個線程將票售出,與此同時第二個線程也已經(jīng)執(zhí)行完成判斷是否有票的操作,并得出結(jié)論票數(shù)大于0,于是它也執(zhí)行售出操作,這樣就會產(chǎn)生負數(shù)。16.6.1線程安全在代碼中判斷當前票數(shù)是否大于0,如果大class
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 水解設備搪砌工沖突管理強化考核試卷含答案
- 動物膠制造工操作管理能力考核試卷含答案
- 自來水筆制造工變革管理能力考核試卷含答案
- 物理性能檢驗員風險評估與管理競賽考核試卷含答案
- 絞盤機司機安全培訓效果測試考核試卷含答案
- 光學鏡頭制造工常識競賽考核試卷含答案
- 麥芽制麥工發(fā)展趨勢能力考核試卷含答案
- 2025年云南外事外語職業(yè)學院單招職業(yè)適應性考試題庫附答案
- 2024年浙江金融職業(yè)學院輔導員考試參考題庫附答案
- 2025年三亞中瑞酒店管理職業(yè)學院輔導員考試參考題庫附答案
- 供應商合規(guī)聲明書標準格式范本
- 軟件工程人員培訓制度
- 2024水電工程陸生野生動物生境保護設計規(guī)范
- 風電場安全警示教育培訓課件
- 【語文】廣東省廣州市天河區(qū)體育東路小學小學二年級上冊期末試卷(含答案)
- 地質(zhì)災害危險性區(qū)域評估服務 方案投標文件(技術(shù)標)
- 高速公路路基施工組織方案
- 藥物中毒指南
- 裝修公司解散協(xié)議書范本
- 七氟丙烷滅火器管理辦法
- 成立教代會活動方案
評論
0/150
提交評論