版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、1第八章第八章 線程線程JAVA語言程序設計2目錄目錄l多線程編程基礎多線程編程基礎l線程的生命周期線程的生命周期l線程的優(yōu)先級線程的優(yōu)先級l本章小結本章小結38.1 多線程編程基礎多線程編程基礎l本節(jié)內容本節(jié)內容 線程的概念 Thread類 Runnable接口 線程間的數(shù)據(jù)共享 多線程的同步控制 線程之間的通信 后臺線程48.1.1 線程的概念線程的概念l進程和線程的區(qū)別進程和線程的區(qū)別l進程進程 一個獨立程序的每一次運行稱為一個進程,例如l用字處理軟件編輯文稿時,同時打開mp3播放程序聽音樂,這兩個獨立的程序在同時運行,稱為兩個進程 設置一個進程要占用相當一部分處理器時間和內存資源 大多
2、數(shù)操作系統(tǒng)不允許進程訪問其他進程的內存空間,進程間的通信很不方便,編程模型比較復雜 多線程編程基礎5l線程線程 一個程序中多段代碼同時并發(fā)執(zhí)行,稱為多線程 通過多線程,一個進程表面上看同時可以執(zhí)行一個以上的任務并發(fā) 創(chuàng)建線程比創(chuàng)建進程開銷要小得多,線程之間的協(xié)作和數(shù)據(jù)交換也比較容易 Java是第一個支持內置線程操作的主流編程語言 多數(shù)程序設計語言支持多線程要借助于操作系統(tǒng)“原語(primitives)”8.1.1 線程的概念線程的概念(續(xù)續(xù))多線程編程基礎68.1.2 Thread類類lThread類類 在Java程序中創(chuàng)建多線程的方法之一是繼承Thread類 封裝了Java程序中一個線程對象
3、需要擁有的屬性和方法 從Thread類派生一個子類,并創(chuàng)建這個子類的對象,就可以產(chǎn)生一個新的線程。這個子類應該重寫Thread類的run方法,在run方法中寫入需要在新線程中執(zhí)行的語句段。這個子類的對象需要調用start方法來啟動,新線程將自動進入run方法。原線程將同時繼續(xù)往下執(zhí)行 Thread類直接繼承了Object類,并實現(xiàn)了Runnable接口。它位于java.lang包中,因而程序開頭不用import任何包就可直接使用多線程編程基礎78.1.2 Thread類類(續(xù)續(xù))例例8_1l在新線程中完成計算某個整數(shù)的階乘在新線程中完成計算某個整數(shù)的階乘public class Ex8_1 p
4、ublic static void main( String args ) System.out.println(main thread starts);FactorialThread thread=new FactorialThread(10); thread.start(); System.out.println(main thread ends ); class FactorialThread extends Thread private int num; public FactorialThread( int num ) this.num=num; 多線程編程基礎8public voi
5、d run() int i=num; int result=1; System.out.println(new thread started ); while(i0) result=result*i; i=i-1; System.out.println(The factorial of +num+ is +result); System.out.println(new thread ends); l運行結果運行結果main thread startsmain thread endsnew thread startedThe factorial of 10 is 3628800new threa
6、d ends8.1.2 Thread類類(續(xù)續(xù))例例8_1運行結果運行結果多線程編程基礎9l結果說明結果說明 main線程已經(jīng)執(zhí)行完后,新線程才執(zhí)行完 main函數(shù)調用thread.start()方法啟動新線程后并不等待其run方法返回就繼續(xù)運行,thread.run函數(shù)在一邊獨自運行,不影響原來的main函數(shù)的運行l(wèi)源程序修改源程序修改 如果啟動新線程后希望主線程多持續(xù)一會再結束,可在start語句后加上讓當前線程(這里當然是main)休息1毫秒的語句:try Thread.sleep(1); catch(Exception e);8.1.2 Thread類類(續(xù)續(xù))例例8_1修改修改多線程
7、編程基礎10l修改后運行結果修改后運行結果main thread startsnew thread staredThe factorial of 10 is 3628800new thread endsmain thread endsl運行結果說明運行結果說明 新線程結束后main線程才結束8.1.2 Thread類類(續(xù)續(xù))例例8_1修改后運行結果修改后運行結果多線程編程基礎118.1.2 Thread類類(續(xù)續(xù))常用常用API函數(shù)函數(shù)名稱說明public Thread()構造一個新的線程對象,默認名為Thread-n,n是從0開始遞增的整數(shù)public Thread(Runnable ta
8、rget)構造一個新的線程對象,以一個實現(xiàn)Runnable接口的類的對象為參數(shù)。默認名為Thread-n,n是從0開始遞增的整數(shù)public Thread(String name) 構造一個新的線程對象,并同時指定線程名public static Thread currentThread()返回當前正在運行的線程對象public static void yield() 使當前線程對象暫停,允許別的線程開始運行public static void sleep(long millis)使當前線程暫停運行指定毫秒數(shù),但此線程并不失去已獲得的鎖旗標。多線程編程基礎12public void start
9、()啟動線程,JVM將調用此線程的run方法,結果是將同時運行兩個線程,當前線程和執(zhí)行run方法的線程public void run()Thread的子類應該重寫此方法,內容應為該線程應執(zhí)行的任務。public final void stop()停止線程運行,釋放該線程占用的對象鎖旗標。public void interrupt()打斷此線程public final void join()在當前線程中加入調用join方法的線程A,直到線程A死亡才能繼續(xù)執(zhí)行當前線程public final void join(long millis)在當前線程中加入調用join方法的線程A,直到到達參數(shù)指定毫秒
10、數(shù)或線程A死亡才能繼續(xù)執(zhí)行當前線程8.1.2 Thread類類(續(xù)續(xù))常用常用API函數(shù)函數(shù)多線程編程基礎13public final void setPriority(int newPriority)設置線程優(yōu)先級public final void setDaemon(Boolean on)設置是否為后臺線程,如果當前運行線程均為后臺線程則JVM停止運行。這個方法必須在start()方法前使用public final void checkAccess()判斷當前線程是否有權力修改調用此方法的線程public void setName(String name)更該本線程的名稱為指定參數(shù)publ
11、ic final boolean isAlive()測試線程是否處于活動狀態(tài),如果線程被啟動并且沒有死亡則返回true8.1.2 Thread類類(續(xù)續(xù))常用常用API函數(shù)函數(shù)多線程編程基礎14l創(chuàng)建創(chuàng)建3個新線程,每個線程睡眠一段時間(個新線程,每個線程睡眠一段時間(06秒),然秒),然后結束后結束public class Ex8_2 public static void main( String args ) /創(chuàng)建并命名每個線程 TestThread thread1 = new TestThread( thread1 ); TestThread thread2 = new TestThr
12、ead( thread2 ); TestThread thread3 = new TestThread( thread3 ); System.out.println( Starting threads ); thread1.start(); / 啟動線程1 thread2.start(); / 啟動線程2 thread3.start(); / 啟動線程3 System.out.println( Threads started, main endsn ); 8.1.2 Thread類類(續(xù)續(xù))例例8_2多線程編程基礎15class TestThread extends Thread privat
13、e int sleepTime; public TestThread( String name ) super( name ); sleepTime = ( int ) ( Math.random() * 6000 ); public void run() try System.out.println( getName() + going to sleep for + sleepTime ); Thread.sleep( sleepTime ); /線程休眠 catch ( InterruptedException exception ) ; System.out.println( getNa
14、me() + finished 8.1.2 Thread類類(續(xù)續(xù))例例8_2多線程編程基礎16l運行結果運行結果Starting threadsThreads started, main endsthread1 going to sleep for 3519thread2 going to sleep for 1689thread3 going to sleep for 5565thread2 finishedthread1 finishedthread3 finishedl說明說明 由于線程3休眠時間最長,所以最后結束,線程2休眠時間最短,所以最先結束 每次運行,都會產(chǎn)生不同的隨機休眠時間
15、,所以結果都不相同8.1.2 Thread類類(續(xù)續(xù))例例8_2運行結果運行結果多線程編程基礎178.1.3 Runnable接口接口lRunnable接口接口 Java多線程機制的一個重要部分,實際上它只有一個run()方法 Thread類實現(xiàn)了Runnable接口,相對于Thread類,它更適合于多個線程處理同一資源 實現(xiàn)Runnable接口的類的對象可以用來創(chuàng)建線程,這時start方法啟動此線程就會在此線程上運行run()方法 在編寫復雜程序時相關的類可能已經(jīng)繼承了某個基類,而Java不支持多繼承,在這種情況下,便需要通過實現(xiàn)Runnable接口來生成多線程多線程編程基礎18l使用使用R
16、unnable接口實現(xiàn)例接口實現(xiàn)例8_1功能功能public class Ex8_1 public static void main( String args ) System.out.println(main thread starts); FactorialThread t=new FactorialThread(10); new Thread(t).start(); System.out.println(new thread started,main thread ends ); 8.1.3 Runnable接口接口(續(xù)續(xù))例例8_3多線程編程基礎19class FactorialThr
17、ead implements Runnable private int num; public FactorialThread( int num ) this.num=num; public void run() int i=num; int result=1; while(i0) result=result*i; i=i-1; System.out.println(The factorial of +num+ is +result); System.out.println(new thread ends); 8.1.3 Runnable接口接口(續(xù)續(xù))例例8_3多線程編程基礎20l使用使用R
18、unnable接口實現(xiàn)例接口實現(xiàn)例8_2功能功能public class Ex8_4 public static void main( String args ) TestThread thread1 = new TestThread(); TestThread thread2 = new TestThread(); TestThread thread3 = new TestThread(); System.out.println( Starting threads ); new Thread(thread1,Thread1).start(); new Thread(thread2,Threa
19、d2).start(); new Thread(thread3,Thread3).start(); System.out.println( Threads started, main endsn ); 8.1.3 Runnable接口接口(續(xù)續(xù))例例8_4多線程編程基礎21class TestThread implements Runnable private int sleepTime; public TestThread() sleepTime = ( int ) ( Math.random() * 6000 ); public void run() try System.out.prin
20、tln( Thread.currentThread().getName() + going to sleep for + sleepTime ); Thread.sleep( sleepTime ); catch ( InterruptedException exception ) ; System.out.println( Thread.currentThread().getName()+ finished ); 8.1.3 Runnable接口接口(續(xù)續(xù))例例8_4多線程編程基礎228.1.4 線程間的數(shù)據(jù)共享線程間的數(shù)據(jù)共享l代碼共享代碼共享 多個線程的執(zhí)行代碼來自同一個類的run方法時
21、,即稱它們共享相同的代碼l數(shù)據(jù)共享數(shù)據(jù)共享 當共享訪問相同的對象時,即它們共享相同的數(shù)據(jù) 使用Runnable接口可以輕松實現(xiàn)多個線程共享相同數(shù)據(jù),只要用同一個實現(xiàn)了Runnable接口的實例作為參數(shù)創(chuàng)建多個線程就可以了多線程編程基礎23l修改例修改例8_4,只用一個,只用一個Runnable類型的對象為參類型的對象為參數(shù)創(chuàng)建數(shù)創(chuàng)建3個新線程。個新線程。public class Ex8_5 public static void main( String args ) TestThread threadobj = new TestThread(); System.out.println( Sta
22、rting threads ); new Thread(threadobj,Thread1).start(); new Thread(threadobj,Thread2).start(); new Thread(threadobj,Thread3).start(); System.out.println( Threads started, main endsn ); 8.1.4 線程間的數(shù)據(jù)共享線程間的數(shù)據(jù)共享(續(xù)續(xù))例例8_5多線程編程基礎24 class TestThread implements Runnable private int sleepTime; public TestThr
23、ead() sleepTime = ( int ) ( Math.random() * 6000 ); public void run() try System.out.println( Thread.currentThread().getName() + going to sleep for + sleepTime ); Thread.sleep( sleepTime ); catch ( InterruptedException exception ) ;System.out.println( Thread.currentThread().getName() + finished ); 8
24、.1.4 線程間的數(shù)據(jù)共享線程間的數(shù)據(jù)共享(續(xù)續(xù))例例8_5多線程編程基礎25l運行結果運行結果Starting threadsThread1 going to sleep for 966Thread2 going to sleep for 966Threads started, main endsThread3 going to sleep for 966Thread1 finishedThread2 finishedThread3 finishedl說明說明 因為是用一個Runnable類型對象創(chuàng)建的3個新線程,這三個線程就共享了這個對象的私有成員sleepTime,在本次運行中,三個線程
25、都休眠了966毫秒8.1.4 線程間的數(shù)據(jù)共享線程間的數(shù)據(jù)共享(續(xù)續(xù)) 例例8_5運行結果運行結果多線程編程基礎268.1.4 線程間的資源共享線程間的資源共享(續(xù)續(xù))l獨立的同時運行的線程有時需要共享一些數(shù)據(jù)并且獨立的同時運行的線程有時需要共享一些數(shù)據(jù)并且考慮到彼此的狀態(tài)和動作考慮到彼此的狀態(tài)和動作 例如生產(chǎn)/消費問題:生產(chǎn)線程產(chǎn)生數(shù)據(jù)流,然后這些數(shù)據(jù)流再被消費線程消費 假設一個Java應用程序,其中有一個線程負責往文件寫數(shù)據(jù),另一個線程從同一個文件中往出都數(shù)據(jù),因為涉及到同一個資源,這里是同一個文件,這兩個線程必須保證某種方式的同步多線程編程基礎27l用三個線程模擬三個售票口,總共出售用三
26、個線程模擬三個售票口,總共出售200張票張票 用3個線程模仿3個售票口的售票行為 這3個線程應該共享200張票的數(shù)據(jù)public class Ex8_6public static void main(String args)SellTickets t=new SellTickets(); new Thread(t).start(); new Thread(t).start();new Thread(t).start();多線程編程基礎8.1.4 線程間的數(shù)據(jù)共享線程間的數(shù)據(jù)共享(續(xù)續(xù))例例8_628class SellTickets implements Runnableprivate int
27、 tickets=200; public void run() while(tickets0) System.out.println( Thread.currentThread().getName() + is selling ticket +tickets-); 8.1.4 線程間的數(shù)據(jù)共享線程間的數(shù)據(jù)共享(續(xù)續(xù))例例8_6多線程編程基礎29l運行結果選最后幾行如下運行結果選最后幾行如下Thread-2 is selling ticket 6Thread-1 is selling ticket 5Thread-0 is selling ticket 4Thread-2 is selling
28、ticket 3Thread-1 is selling ticket 2Thread-0 is selling ticket 1l說明說明 在這個例子中,創(chuàng)建了3個線程,每個線程調用的是同一個SellTickets對象中的run()方法,訪問的是同一個對象中的變量(tickets) 如果是通過創(chuàng)建Thread類的子類來模擬售票過程,再創(chuàng)建3個新線程,則每個線程都會有各自的方法和變量,雖然方法是相同的,但變量卻是各有200張票,因而結果將會是各賣出200張票,和原意就不符了8.1.4 線程間的數(shù)據(jù)共享線程間的數(shù)據(jù)共享(續(xù)續(xù)) 例例8_6運行結果運行結果多線程編程基礎308.1.5 多線程的同步控
29、制多線程的同步控制l有時線程之間彼此不獨立、需要同步有時線程之間彼此不獨立、需要同步 線程間的互斥l同時運行的幾個線程需要共享一個(些)數(shù)據(jù)l一個線程對共享的數(shù)據(jù)進行操作時,不允許其他線程打斷它,否則會破壞數(shù)據(jù)的完整性。即被多個線程共享的數(shù)據(jù),在某一時刻只允許一個線程對其進行操作 “生產(chǎn)者/消費者” 問題l生產(chǎn)者產(chǎn)生數(shù)據(jù),消費者消費數(shù)據(jù),具體來說,假設有一個Java應用程序,其中有一個線程負責往數(shù)據(jù)區(qū)寫數(shù)據(jù),另一個線程從同一數(shù)據(jù)區(qū)中讀數(shù)據(jù),兩個線程可以并行執(zhí)行(類似于流水線上的兩道工序)l如果數(shù)據(jù)區(qū)已滿,生產(chǎn)者要等消費者取走一些數(shù)據(jù)后才能再放;而當數(shù)據(jù)區(qū)沒有數(shù)據(jù)時,消費者要等生產(chǎn)者放入一些數(shù)據(jù)
30、后再取多線程編程基礎31l用兩個線程模擬存票、售票過程用兩個線程模擬存票、售票過程 假定開始售票處并沒有票,一個線程往里存票,另外一個線程則往出賣票 我們新建一個票類對象,讓存票和售票線程都訪問它。本例采用兩個線程共享同一個數(shù)據(jù)對象來實現(xiàn)對同一份數(shù)據(jù)的操作public class Ex8_7 public static void main(String args) Tickets t=new Tickets(10); new Consumer(t).start();new Producer(t).start(); 8.1.5 多線程的同步控制多線程的同步控制(續(xù)續(xù)) 例例8_7多線程編程基礎3
31、2class Tickets int number=0; /票號int size; /總票數(shù)boolean available=false; /表示目前是否有票可售public Tickets(int size) /構造函數(shù),傳入總票數(shù)參數(shù)this.size=size; 8.1.5 多線程的同步控制多線程的同步控制(續(xù)續(xù)) 例例8_7多線程編程基礎33class Producer extends ThreadTickets t=null;public Producer(Tickets t)this.t=t; public void run()while( t.number t.size) Sy
32、stem.out.println(Producer puts ticket +(+t.number); t.available=true; 8.1.5 多線程的同步控制多線程的同步控制(續(xù)續(xù)) 例例8_7多線程編程基礎34class Consumer extends Thread /售票線程售票線程Tickets t=null;int i=0;public Consumer(Tickets t) this.t=t; public void run()while(it.size) if(t.available=true & i=t.number) System.out.println(Consu
33、mer buys ticket +(+i); if(i=t.number) t.available=false;8.1.5 多線程的同步控制多線程的同步控制(續(xù)續(xù)) 例例8_7多線程編程基礎35l運行結果運行結果Producer puts ticket 1Producer puts ticket 2Producer puts ticket 3Producer puts ticket 4Producer puts ticket 5Producer puts ticket 6Producer puts ticket 7Producer puts ticket 8Consumer buys tick
34、et 1Consumer buys ticket 2Consumer buys ticket 3Consumer buys ticket 4Consumer buys ticket 5Consumer buys ticket 6Consumer buys ticket 7Consumer buys ticket 8Producer puts ticket 9Producer puts ticket 10Consumer buys ticket 9Consumer buys ticket 10.l通過讓兩個線程操縱同一個票類對象,實現(xiàn)了數(shù)據(jù)共享的目的通過讓兩個線程操縱同一個票類對象,實現(xiàn)了數(shù)據(jù)共
35、享的目的8.1.5 多線程的同步控制多線程的同步控制(續(xù)續(xù)) 例例8_7運行結果運行結果多線程編程基礎36l設想一下,假如售票線程運行到設想一下,假如售票線程運行到t.available=false之前,之前,CPU切換切換到存票線程,存票線程將到存票線程,存票線程將available置為置為true,并直到整個存票線程,并直到整個存票線程結束。再次切換到售票線程后,售票線程執(zhí)行結束。再次切換到售票線程后,售票線程執(zhí)行t.available=false。此時售票號小于存票數(shù),且存票線程已經(jīng)結束不再能將此時售票號小于存票數(shù),且存票線程已經(jīng)結束不再能將t.available置為置為true,則售票
36、線程陷入了死循環(huán),則售票線程陷入了死循環(huán)l如果我們在如果我們在t.available=false之前加上之前加上sleep語句,讓售票線程多停語句,讓售票線程多停留一會,則可以更加清楚地看到這個問題留一會,則可以更加清楚地看到這個問題if(i=t.number) try Thread.sleep(1); catch ( InterruptedException exception ) ;t.available=false;8.1.5 多線程的同步控制多線程的同步控制(續(xù)續(xù)) 例例8_7修改修改多線程編程基礎37l修改后運行結果修改后運行結果Producer puts ticket 1Produ
37、cer puts ticket 2Producer puts ticket 3Producer puts ticket 4Producer puts ticket 5Producer puts ticket 6Producer puts ticket 7Producer puts ticket 8Consumer buys ticket 1Consumer buys ticket 2Consumer buys ticket 3Consumer buys ticket 4Consumer buys ticket 5Consumer buys ticket 6Consumer buys ticke
38、t 7Consumer buys ticket 8Producer puts ticket 9Producer puts ticket 108.1.5 多線程的同步控制多線程的同步控制(續(xù)續(xù))例例8_7修改后運行結果修改后運行結果多線程編程基礎38l如何避免上面這種意外,讓我們的程序是如何避免上面這種意外,讓我們的程序是“線程線程安全安全”的呢?的呢? 解決線程的同步/互斥問題 存票線程和售票線程應保持互斥關系。即售票線程執(zhí)行時不進入存票線程、存票線程執(zhí)行時不進入售票線程lJava 使用的同步機制是監(jiān)視器使用的同步機制是監(jiān)視器 每個對象都只有一個“鎖旗標”與之相連,利用多線程對其的爭奪可實現(xiàn)線
39、程間的互斥操作 當線程A獲得了一個對象的鎖旗標后,線程B必須等待線程A完成規(guī)定的操作、并釋放出鎖旗標后,才能獲得該對象的鎖旗標,并執(zhí)行線程B中的操作8.1.5 多線程的同步控制多線程的同步控制(續(xù)續(xù)) 解決例解決例8_7的問題的問題多線程編程基礎39l線程同步的概念,包括互斥和協(xié)作線程同步的概念,包括互斥和協(xié)作 互斥:許多線程在同一個共享數(shù)據(jù)上操作而互不干擾,同一時刻只能有一個線程訪問該共享數(shù)據(jù)。因此有些方法或程序段在同一時刻只能被一個線程執(zhí)行,稱之為監(jiān)視區(qū) 協(xié)作:多個線程可以有條件地同時操作共享數(shù)據(jù)。執(zhí)行監(jiān)視區(qū)代碼的線程在條件滿足的情況下可以允許其它線程進入監(jiān)視區(qū)8.1.5 多線程的同步控制
40、多線程的同步控制(續(xù)續(xù))線程同步線程同步(Synchronization)多線程編程基礎40lsynchronized 線程同步關鍵字線程同步關鍵字 用于指定需要同步的代碼段或方法,也就是監(jiān)視區(qū) 可實現(xiàn)與一個鎖旗標的交互。例如:lsynchronized(對象) 代碼段 synchronized的功能是:首先判斷對象的鎖旗標是否在,如果在就獲得鎖旗標,然后就可以執(zhí)行緊隨其后的代碼段;如果對象的鎖旗標不在(已被其他線程拿走),就進入等待狀態(tài),直到獲得鎖旗標 當被synchronized限定的代碼段執(zhí)行完,就釋放鎖旗標8.1.5 多線程的同步控制多線程的同步控制(續(xù)續(xù)) synchronized關
41、鍵字關鍵字多線程編程基礎41l將需要互斥的語句段放入將需要互斥的語句段放入synchronized(object)語句框中,語句框中,且兩處的且兩處的object是相同的是相同的class Producer extends Thread Tickets t=null;public Producer(Tickets t) this.t=t;public void run() while(t.number)t.size) synchronized(t) / 申請對象t的鎖旗標 System.out.println(Producer puts ticket +(+t.number); t.avail
42、able=true; / 釋放對象t的鎖旗標System.out.println(Producer ends!);8.1.5 多線程的同步控制多線程的同步控制(續(xù)續(xù)) synchronized關鍵字關鍵字多線程編程基礎42class Consumer extends Thread Tickets t=null;int i=0;public Consumer(Tickets t) this.t=t; public void run() while(it.size) synchronized(t) /申請對象t的鎖旗標 if(t.available=true & i=t.number) Syste
43、m.out.println(Consumer buys ticket +(+i); if(i=t.number) tryThread.sleep(1);catch(Exception e) t.available=false; /釋放對象t的鎖旗標System.out.println(Consumer ends); 8.1.5 多線程的同步控制多線程的同步控制(續(xù)續(xù)) synchronized關鍵字關鍵字多線程編程基礎43l說明說明 存票程序段和售票程序段為獲得同一對象的鎖旗標而實現(xiàn)互斥操作 當線程執(zhí)行到synchronized的時候,檢查傳入的實參對象,并申請得到該對象的鎖旗標。如果得不到,
44、那么線程就被放到一個與該對象鎖旗標相對應的等待線程池中。直到該對象的鎖旗標被歸還,池中的等待線程才能重新去獲得鎖旗標,然后繼續(xù)執(zhí)行下去 除了可以對指定的代碼段進行同步控制之外,還可以定義整個方法在同步控制下執(zhí)行,只要在方法定義前加上synchronized關鍵字即可8.1.5 多線程的同步控制多線程的同步控制(續(xù)續(xù)) synchronized關鍵字關鍵字多線程編程基礎44l實現(xiàn)例實現(xiàn)例8_7功能。將互斥方法放在共享的資源類功能。將互斥方法放在共享的資源類Tickets中中class Tickets int size; /票總數(shù)int number=0; /存票序號int i=0; /售票序號b
45、oolean available=false; /是否有待售的票public Tickets(int size) this.size=size; public synchronized void put() /同步方法,實現(xiàn)存票的功能 System.out.println(Producer puts ticket +(+number); available=true;public synchronized void sell() /同步方法,實現(xiàn)售票的功能 if(available=true & isize表示售票結束8.1.6 線程之間的通信線程之間的通信(續(xù)續(xù)) 例例8_9多線程編程基礎4
46、9class Producer extends Thread Tickets t=null;public Producer(Tickets t) this.t=t; public void run() while(t.numbert.size) t.put();class Consumer extends Thread Tickets t=null;public Consumer(Tickets t) this.t=t; public void run() while(t.number=t.size) t.sell(); 8.1.6 線程之間的通信線程之間的通信(續(xù)續(xù)) 例例8_9多線程編程基
47、礎50l運行結果運行結果Producer puts ticket 1Consumer buys ticket 1Producer puts ticket 2Consumer buys ticket 2Producer puts ticket 3Consumer buys ticket 3Producer puts ticket 4Consumer buys ticket 4Producer puts ticket 5Consumer buys ticket 5Producer puts ticket 6Consumer buys ticket 6Producer puts ticket 7Co
48、nsumer buys ticket 7Producer puts ticket 8Consumer buys ticket 8Producer puts ticket 9Consumer buys ticket 9Producer puts ticket 10Consumer buys ticket 108.1.6 線程之間的通信線程之間的通信(續(xù)續(xù)) 例例8_9運行結果運行結果多線程編程基礎51l程序說明程序說明 當Consumer線程售出票后,available值變?yōu)閒alse,當Producer線程放入票后,available值變?yōu)閠rue 只有available為true時,Cons
49、umer線程才能售票,否則就必須等待Producer線程放入新的票后的通知 只有available為false時,Producer線程才能放票,否則必須等待Consumer線程售出票后的通知 可見通過線程間的通信實現(xiàn)了我們的要求8.1.6 線程之間的通信線程之間的通信(續(xù)續(xù)) 例例8_9說明說明多線程編程基礎528.1.7 后臺線程后臺線程l后臺線程后臺線程 也叫守護線程,通常是為了輔助其它線程而運行的線程 它不妨礙程序終止 一個進程中只要還有一個前臺線程在運行,這個進程就不會結束;如果一個進程中的所有前臺線程都已經(jīng)結束,那么無論是否還有未結束的后臺線程,這個進程都會結束 “垃圾回收”便是一個
50、后臺線程 如果對某個線程對象在啟動(調用start方法)之前調用了setDaemon(true)方法,這個線程就變成了后臺線程多線程編程基礎53l創(chuàng)建一個無限循環(huán)的后臺線程,驗證主線程結束后,程序即創(chuàng)建一個無限循環(huán)的后臺線程,驗證主線程結束后,程序即結束結束public class Ex8_10 public static void main(String args) ThreadTest t=new ThreadTest();t.setDaemon(true);t.start();class ThreadTest extends Thread public void run() while(
51、true) l運行程序,則發(fā)現(xiàn)整個程序在主線程結束時就隨之中止運行運行程序,則發(fā)現(xiàn)整個程序在主線程結束時就隨之中止運行了,如果注釋掉了,如果注釋掉t.setDaemon(true)語句,則程序永遠不會語句,則程序永遠不會結束結束8.1.7 后臺線程后臺線程(續(xù)續(xù)) 例例8_10多線程編程基礎548.2 線程的生命周期線程的生命周期l線程的生命周期線程的生命周期 線程從產(chǎn)生到消亡的過程 一個線程在任何時刻都處于某種線程狀態(tài)(thread state) 558.2.1 線程的幾種基本狀態(tài)線程的幾種基本狀態(tài)l線程生命周期狀態(tài)圖線程生命周期狀態(tài)圖 線程調度 run()完成 時間到 synchroniz
52、ed未獲得鎖旗標 notify() wait() sleep() 獲得鎖旗標 start() 就緒狀態(tài) 新線程 阻塞(休眠) 阻塞(進入對象lock池) 運行狀態(tài) 阻塞(進入對象wait 池) 死亡狀態(tài) 線程的生命周期56l誕生狀態(tài)誕生狀態(tài) 線程剛剛被創(chuàng)建l就緒狀態(tài)就緒狀態(tài) 線程的 start 方法已被執(zhí)行 線程已準備好運行l(wèi)運行狀態(tài)運行狀態(tài) 處理機分配給了線程,線程正在運行l(wèi)阻塞狀態(tài)(阻塞狀態(tài)(Blocked) 在線程發(fā)出輸入/輸出請求且必須等待其返回 遇到用synchronized標記的方法而未獲得其監(jiān)視器暫時不能進入執(zhí)行時l休眠狀態(tài)(休眠狀態(tài)(Sleeping) 執(zhí)行sleep方法而進入
53、休眠l死亡狀態(tài)死亡狀態(tài) 線程已完成或退出8.2.1 線程的幾種基本狀態(tài)線程的幾種基本狀態(tài)(續(xù)續(xù))線程的生命周期578.2.2 死鎖問題死鎖問題l死鎖死鎖 線程在運行過程中,其中某個步驟往往需要滿足一些條件才能繼續(xù)進行下去,如果這個條件不能滿足,線程將在這個步驟上出現(xiàn)阻塞 線程A可能會陷于對線程B的等待,而線程B同樣陷于對線程C的等待,依次類推,整個等待鏈最后又可能回到線程A。如此一來便陷入一個彼此等待的輪回中,任何線程都動彈不得,此即所謂死鎖(deadlock) 對于死鎖問題,關鍵不在于出現(xiàn)問題后調試,而是在于預防線程的生命周期58l設想一個游戲,規(guī)則設想一個游戲,規(guī)則為為3個人站在三角形的個
54、人站在三角形的三個頂點的位置上,三個頂點的位置上,三個邊上放著三個球,三個邊上放著三個球,如圖所示。每個人都如圖所示。每個人都必須先拿到自己左手必須先拿到自己左手邊的球,才能再拿到邊的球,才能再拿到右手邊的球,兩手都右手邊的球,兩手都有球之后,才能夠把有球之后,才能夠把兩個球都放下兩個球都放下Player_0Player_1Player_20218.2.2 死鎖問題死鎖問題(續(xù)續(xù)) 例例8_11線程的生命周期59l例例8_11 創(chuàng)建創(chuàng)建3個線程模擬個線程模擬3個游戲者的行為。個游戲者的行為。public class Ex8_11public static void main(String ar
55、gs)Balls ball=new Balls(); /新建一個球類對象Player0 p0=new Player0(ball); /創(chuàng)建0號游戲者Player1 p1=new Player1(ball); /創(chuàng)建1號游戲者Player2 p2=new Player2(ball); /創(chuàng)建2號游戲者p0.start(); /啟動0號游戲者p1.start(); /啟動1號游戲者p2.start(); /啟動2號游戲者class Balls /球類boolean flag0=false; /0號球的標志變量,true表示已被人拿,false表示未被任何人拿boolean flag1=false;
56、 /1號球的標志變量boolean flag2=false; /2號球的標志變量8.2.2 死鎖問題死鎖問題(續(xù)續(xù)) 例例8_11線程的生命周期60class Player0 extends Thread /0號游戲者的類private Balls ball;public Player0(Balls b) this.ball=b; public void run() while(true) while(ball.flag1=true); /如果1號球已被拿走,則等待 ball.flag1=true; /拿起1號球 while(ball.flag0=true); /如果0號球已被拿走,則等待 i
57、f(ball.flag1=true & ball.flag0=false) ball.flag0=true; /拿起0號球 System.out.println(Player0 has got two balls!); ball.flag1=false; /放下1號球 ball.flag0=false; /放下0號球 try sleep(1);catch(Exception e); /放下后休息1ms 8.2.2 死鎖問題死鎖問題(續(xù)續(xù)) 例例8_11線程的生命周期61l運行結果運行結果 若干次后將陷入死鎖,不再有輸出信息,即任何人都不能再同時擁有兩側的球l程序說明程序說明 如果剛好3個人都拿
58、到了左手邊的球,都等待那右手邊的球,則因為誰都不能放手,則這3個線程都將陷入無止盡的等待當中,這就構成了死鎖 為了便于觀察死鎖發(fā)生的條件,我們在每個游戲者放下兩邊的球后增加了sleep語句 為了避免死鎖,需要修改游戲規(guī)則,使每個人都只能先搶到兩側中號比較小的球,才能拿另一只球,這樣就不會再出現(xiàn)死鎖現(xiàn)象8.2.2 死鎖問題死鎖問題(續(xù)續(xù)) 例例8_11運行結果運行結果線程的生命周期628.2.3 控制線程的生命控制線程的生命l結束線程的生命結束線程的生命 用stop方法可以結束線程的生命l但如果一個線程正在操作共享數(shù)據(jù)段,操作過程沒有完成就用stop結束的話,將會導致數(shù)據(jù)的不完整,因此并不提倡使
59、用此方法 通常,可通過控制run方法中循環(huán)條件的方式來結束一個線程線程的生命周期63l線程不斷顯示遞增整數(shù),按下回車鍵則停止執(zhí)行線程不斷顯示遞增整數(shù),按下回車鍵則停止執(zhí)行import java.io.*;public class Ex8_12public static void main(String args) throws IOException TestThread t=new TestThread(); t.start(); new BufferedReader(new InputStreamReader(System.in) .readLine(); /等待鍵盤輸入 t.stopme
60、(); /調用stopme方法結束t線程8.2.3 控制線程的生命控制線程的生命(續(xù)續(xù)) 例例8_12線程的生命周期64class TestThread extends Threadprivate boolean flag=true;public void stopme() /在此方法中控制循環(huán)條件flag=false;public void run() int i=0;while(flag) System.out.println(i+); /如果flag為真則一直顯示遞增整數(shù)l運行效果為按下回車鍵后則停止顯示運行效果為按下回車鍵后則停止顯示8.2.3 控制線程的生命控制線程的生命(續(xù)續(xù)) 例
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年勞務服務協(xié)議
- 高級橡木地板銷售協(xié)議(2024年)
- 2024年電力施工項目商務協(xié)議樣本
- 房屋二手交易協(xié)議模板(簡化)
- 數(shù)智驅動研究生教育治理重構的實踐與分析
- 2024年度戶外活動場地租賃合作協(xié)議
- 船舶買賣專項協(xié)議:家用游艇交易條款
- 辦公物資采購協(xié)議:2024年詳規(guī)
- 2024年度服務項目合作采購協(xié)議
- 2024專業(yè)零售店長聘任協(xié)議
- 文網(wǎng)文業(yè)務發(fā)展報告(XX單位)
- 硬筆書法章法課件
- 養(yǎng)老院老人入院風險告知書4篇
- 智能制造專業(yè)群建設(智能制造業(yè)專業(yè)技術學校創(chuàng)業(yè)計劃)課件整理
- 鋼直梯安全驗收(檢查)表
- 設備基礎施工方案及安全措施
- 用電安全檢查記錄表
- 部編版語文六年級上冊《口語交際》專項練習
- (完整)人教中學高中生物實驗目錄
- 人教人音版八年級音樂上冊《青藏高原》課件(精選)
- 淚道阻塞課件
評論
0/150
提交評論