




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領
文檔簡介
1、案例:實現(xiàn)學生成績查詢系統(tǒng)的實時互動案例:實現(xiàn)學生成績查詢系統(tǒng)的實時互動聊天功能聊天功能 。 浙江工業(yè)大學浙江工業(yè)大學 計算機學院計算機學院趙小敏趙小敏涉及知識點n1、線程程序、線程程序的創(chuàng)建n2、線程的生命周期、線程的生命周期n3、多線程的同步處理、多線程的同步處理n4、基于、基于TCP的網(wǎng)絡程序設計的網(wǎng)絡程序設計8.1線程的創(chuàng)建n編寫多線程程序是為了提高資源的利用率或提高編寫多線程程序是為了提高資源的利用率或提高程序的運行效率或更好地監(jiān)控程序的運行過程。程序的運行效率或更好地監(jiān)控程序的運行過程。n編寫線程的方法:編寫線程的方法:1.通過實現(xiàn)接口通過實現(xiàn)接口java.lang.Runnabl
2、e創(chuàng)建線程創(chuàng)建線程2.通過繼承類通過繼承類java.lang.Thread創(chuàng)建線程創(chuàng)建線程1、通過實現(xiàn)、通過實現(xiàn)Runnable接口創(chuàng)建線程接口創(chuàng)建線程nRunnable 接口只提供了一個接口只提供了一個public void run( )方法。方法。n創(chuàng)建線程的方法:創(chuàng)建線程的方法:定義一個類實現(xiàn)定義一個類實現(xiàn)Runnable接口。接口。將該類的實例作為參數(shù)傳給將該類的實例作為參數(shù)傳給Thread類的一個類的一個構(gòu)造函數(shù),從而創(chuàng)建一個線程。構(gòu)造函數(shù),從而創(chuàng)建一個線程。例:用用Runnable創(chuàng)建線程的示例創(chuàng)建線程的示例1.public class ThreadTest2. public st
3、atic void main(String args )3.Thread t1 = new Thread( new Hello( ) );4.Thread t2 = new Thread( new Hello( ) );5.t1.start( );6.t2.start( );7.8.9.class Hello implements Runnable10. int i ;11. public void run( )12.while( true)13.System.out.println(Hello:+i+);14.if (i=5) break ;15.16. 17. 2、通過繼承類、通過繼承類T
4、hread創(chuàng)建線程創(chuàng)建線程nThread 類本身實現(xiàn)了類本身實現(xiàn)了Runnable接口。接口。n在在Thread類中,類中,run方法被實現(xiàn)為空方法。方法被實現(xiàn)為空方法。nThread中的構(gòu)造方法:中的構(gòu)造方法:public Thread( );public Thread(String name)public Thread(Runnable target)public Thread(Runnable target, String name)public Thread(ThreadGroup group, Runnable target)public Thread(ThreadGroup gro
5、up, String name)public Thread(ThreadGroup group, Runnable target, String name)通過繼承通過繼承Thread類來創(chuàng)建線程的步驟類來創(chuàng)建線程的步驟(1)通過繼承通過繼承Thread類,重寫類,重寫run( )方法來定義線程體。方法來定義線程體。public class Counter extends Thread public void run( ) (2)創(chuàng)建該子類的對象創(chuàng)建線程。創(chuàng)建該子類的對象創(chuàng)建線程。創(chuàng)建與運行線程:創(chuàng)建與運行線程:Counter ThreadCounter = new Counter( );Th
6、readCounter.Start( );例例2:通過繼承:通過繼承Thread類創(chuàng)建線程類創(chuàng)建線程1.public class ThreadTest2 2. public static void main(String args )3. Hello t1 = new Hello( );4. Hello t2 = new Hello( );5. t1.start( );6. t2.start( );7. 8.9.class Hello extends Thread10. int i ;11. public void run( )12.while( true)13.System.out.prin
7、tln(Hello+i+);14.if (i=5) break ;15.16. 17. 兩種創(chuàng)建線程方法比較兩種創(chuàng)建線程方法比較n實現(xiàn)實現(xiàn)Runnable接口的優(yōu)勢:接口的優(yōu)勢:q符合符合OO設計的思想。設計的思想。q便于用便于用extends繼承其它類。繼承其它類。n采用繼承采用繼承Thread類方法的優(yōu)點:程序代碼更簡單。類方法的優(yōu)點:程序代碼更簡單。n提倡采用第一種方式。提倡采用第一種方式。n通過通過Thread實例的實例的start(),一個,一個Thread的實例只能的實例只能產(chǎn)生一個線程產(chǎn)生一個線程n Runnable的實例是可運行的,但它自己并不能直接的實例是可運行的,但它自己并
8、不能直接運行,它需要被運行,它需要被Thread對象來包裝才行運行對象來包裝才行運行 ,但同同一實例一實例(Runnable實例實例)可產(chǎn)生多個線程可產(chǎn)生多個線程例例3:兩種創(chuàng)建線程方法比較:兩種創(chuàng)建線程方法比較1.class MyThread extends Thread2. public int x = 0;3. public void run()4. System.out.println(+x);5. 6.7.class R implements Runnable8. private int x = 0;9. public void run()10. System.out.println
9、(+x);11. 12.例例3:兩種創(chuàng)建線程方法比較:兩種創(chuàng)建線程方法比較(續(xù)續(xù))13.public class TestMyThread 14. public static void main(String args) throws Exception 15. for(int i=0;i10;i+)16. Thread t = new MyThread();17. t.start();18. 19. Thread.sleep(5000);/休眠休眠5s,讓上面的線程運行完成讓上面的線程運行完成 20. R r = new R();21. for(int i=0;i10;i+)22. Thre
10、ad t = new Thread(r);23. t.start();24. 25. 26.8.2線程的生命周期線程的生命周期就緒就緒運行運行阻塞阻塞死亡死亡阻塞在阻塞在對象鎖池對象鎖池阻塞在阻塞在對象等待池對象等待池新建新建Start( )線程調(diào)度yield( )Sleep() or join( )run( ) endswait( )Synchronized( )Lock可用notify( )interrupt( )Sleep( ) timeoutThread joins orinterrupt線程的基本控制方法nsleep( )nsetPriority()和和getPriority()ny
11、ield( )njoin( )ninterrupt()ncurrentThread()nisAlive()nisDaemon()和和setDaemon()nstop() 過時過時類類Thread的方法的方法sleep( )方法方法n該方法用來使一個線程暫停運行一段固定的時間。該方法用來使一個線程暫停運行一段固定的時間。n在線程睡眠時間內(nèi),將運行別的線程。在線程睡眠時間內(nèi),將運行別的線程。nsleep( ) 結(jié)束后,線程將進入結(jié)束后,線程將進入就緒就緒(Runnable)狀態(tài)。狀態(tài)。nsleep()方法的格式有以下兩種:方法的格式有以下兩種:static void sleep(int mills
12、) throws InterruptedException /休眠時間以休眠時間以ms為單位為單位static void sleep(long millis, int nanos) throws InterruptedException /休眠時間,指毫秒數(shù)與納休眠時間,指毫秒數(shù)與納秒數(shù)之和秒數(shù)之和例例4:每隔一秒鐘打印一個數(shù)字到屏幕每隔一秒鐘打印一個數(shù)字到屏幕1.public class Timer extends T time=1;3.public Timer(int time)4.this.time=time;5.6. public void run()7. try8
13、. for(int i=1;i=time;i+) 9. Thread.sleep(1000);10. System.out.println(i);11. 12. catch(InterruptedException e)13. System.out.println(e.toString();14. 15. 16.public static void main(String args)17.Timer timer=new Timer(10);18.timer.start();19. 20.線程優(yōu)先級控制:線程優(yōu)先級控制:setPriority()和和getPriority()n某一時刻一個某一時
14、刻一個CPUCPU只執(zhí)行一個線程只執(zhí)行一個線程, ,調(diào)度策略為調(diào)度策略為固定優(yōu)先級調(diào)度策略,優(yōu)先級高的先被調(diào)度固定優(yōu)先級調(diào)度策略,優(yōu)先級高的先被調(diào)度l級別有級別有: MIN_PRIORITY : MIN_PRIORITY 最小值為最小值為1 1 NORM_PRIORITY NORM_PRIORITY 默認值,值為默認值,值為5 5 MAX_PRIORITY MAX_PRIORITY 最大值為最大值為1010l利用利用setPrioritysetPriority()()和和getPrioritygetPriority()()方法設置方法設置和獲得線程的優(yōu)先級和獲得線程的優(yōu)先級多線程的執(zhí)行順序多線
15、程的執(zhí)行順序n多個線程運行時,調(diào)度策略為固定優(yōu)先級調(diào)度,多個線程運行時,調(diào)度策略為固定優(yōu)先級調(diào)度,級別相同時,由操作系統(tǒng)按時間片來分配。級別相同時,由操作系統(tǒng)按時間片來分配。n下面給出的例子中下面給出的例子中,共運行三個線程共運行三個線程,它們做同它們做同樣的事樣的事, 每次打印循環(huán)次數(shù)和自己的序列號每次打印循環(huán)次數(shù)和自己的序列號,運運行結(jié)果表明行結(jié)果表明,它們并不是連續(xù)運行的。它們并不是連續(xù)運行的。例例5:多個進程運行時執(zhí)行順序是交叉的:多個進程運行時執(zhí)行順序是交叉的1.class MultiThread extends Thread2. int threadNum;3. public st
16、atic void main(String args)4. multithread array=new MultiThread 3;5. for (int i=0;i3;i+) 6. arrayi=new MultiThread (i+1);7. for (int i=0;i3;i+) 8. arrayi.start(); 9. 10. MultiThread (int SerialNum)11. super(); 12. threadNum=SerialNum; 13. 例例5:多個進程運行時執(zhí)行順序是交叉的:多個進程運行時執(zhí)行順序是交叉的14. public void run()15. i
17、nt MySerialNum=0;16. for(int j=1;j=10;j+)17. MySerialNum+;18. try19. Thread.sleep(1000);20. catch(Exception e)21. System.out.println(e.toString();22. 23. System.out.println(loop: + MySerialNum);24. System.out.println(thread:+threadNum+ bye.); 25. 26. 27.yield( )方法方法n調(diào)用該方法將調(diào)用該方法將CPU讓給具有與當前線程相同讓給具有與當前
18、線程相同優(yōu)先級的線程。優(yōu)先級的線程。n如果沒有同等優(yōu)先級的線程是就緒如果沒有同等優(yōu)先級的線程是就緒(Runnable)狀態(tài),狀態(tài),yield( )方法將什么也不做。方法將什么也不做。yield( )方法方法n調(diào)用該方法將調(diào)用該方法將CPU讓給具有與當前線程相同優(yōu)讓給具有與當前線程相同優(yōu)先級的線程。先級的線程。n如果沒有同等優(yōu)先級的線程是如果沒有同等優(yōu)先級的線程是Runnable狀態(tài),狀態(tài),yield( )方法將什么也不做。方法將什么也不做。join( )方法方法nt.join( )方法使當前的線程等待,直到方法使當前的線程等待,直到 t 結(jié)束為止,結(jié)束為止,線程恢復到線程恢復到運行運行(run
19、nable)狀態(tài)。狀態(tài)。n有三種調(diào)用格式:有三種調(diào)用格式:join():如當前線程發(fā)出調(diào)用如當前線程發(fā)出調(diào)用t.join(),則當前線程將,則當前線程將等待線程等待線程 t結(jié)束后在繼續(xù)執(zhí)行。結(jié)束后在繼續(xù)執(zhí)行。join(long millis):如當前線程發(fā)出調(diào)用如當前線程發(fā)出調(diào)用t.join(),則當,則當前線程將等待線程前線程將等待線程t結(jié)束或最多等待結(jié)束或最多等待mills毫秒后在繼毫秒后在繼續(xù)執(zhí)行。續(xù)執(zhí)行。join(long millis,long nanos) throws InterruptedException例6: join( )方法的使用例子方法的使用例子1.public cl
20、ass ThreadJoinTest 2. public static void main(String args )3.int i=0;4.Hello t = new Hello( );5.t.start( );6.while( true)7.System.out.println(Good Morning+i+);8.if (i = 2 & t.isAlive()9. System.out.println(Main waiting for Hello!);10. try11. t.join( ); /等待等待t運行結(jié)束運行結(jié)束12. catch(InterruptedExceptio
21、n e)13. 14. 15.if (i=5) break ;16.17.18.例6: join( )方法的使用例子方法的使用例子(續(xù)續(xù))19.class Hello extends Thread20. int i ;21. public void run( )22.while( true)23.System.out.println(Hello+i+);24.if (i=5) break ;25.26. 27. interrupt()方法:中斷線程方法:中斷線程n如果一個線程如果一個線程t在調(diào)用在調(diào)用sleep()、join()、wait()等方法被阻塞時,則等方法被阻塞時,則err
22、upt()方法將中斷方法將中斷t的阻塞狀態(tài),并將接收到的阻塞狀態(tài),并將接收到InterruptException對象。對象。currentThread()方法:獲取當前線程方法:獲取當前線程nThread 類的靜態(tài)方法類的靜態(tài)方法currentThread( )返回返回當前線程。當前線程。isAlive()方法方法n當線程的狀態(tài)未知時,用當線程的狀態(tài)未知時,用isAlive()確定線程是確定線程是否活著。否活著。n返回返回true 意味著線程已經(jīng)啟動,但還沒有運意味著線程已經(jīng)啟動,但還沒有運行結(jié)束。行結(jié)束。isDaemon()和和setDaemon()方法方法n線程可分為守護線程與用戶線程線程
23、可分為守護線程與用戶線程n當一個程序中只有一個守護線程在運行,程序會當一個程序中只有一個守護線程在運行,程序會立即退出;如果一個程序還有正在運行的用戶線立即退出;如果一個程序還有正在運行的用戶線程,該程序不會中止。程,該程序不會中止。n判斷一個線程是用戶線程還是守護線程,用判斷一個線程是用戶線程還是守護線程,用isDaemon()方法方法n將一個線程設置為守護線程用將一個線程設置為守護線程用setDaemon()方法方法結(jié)束線程結(jié)束線程stop()n線程完成運行并結(jié)束后,將不能再運行。線程完成運行并結(jié)束后,將不能再運行。n除正常運行結(jié)束外,還可用其他方法控制使其除正常運行結(jié)束外,還可用其他方法
24、控制使其停止。停止。q用用stop( )方法方法,已過時已過時 強行終止線程,容易造成線程的不一致。強行終止線程,容易造成線程的不一致。q使用標志使用標志flag。 通過設置通過設置flag 指明指明run( )方法應該結(jié)束。方法應該結(jié)束。例例7:結(jié)束線程的控制:結(jié)束線程的控制8.3 多線程的同步處理多線程的同步處理n多個線程在并發(fā)地運行時可能共用資源。多個線程在并發(fā)地運行時可能共用資源。n多個線程并發(fā)執(zhí)行時,線程的相對執(zhí)行順序是多個線程并發(fā)執(zhí)行時,線程的相對執(zhí)行順序是不確定的;不確定的;n多線程對共享數(shù)據(jù)操作時,這種線程運行順序多線程對共享數(shù)據(jù)操作時,這種線程運行順序的不確定性將會產(chǎn)生執(zhí)行結(jié)
25、果的不確定性,使的不確定性將會產(chǎn)生執(zhí)行結(jié)果的不確定性,使共享數(shù)據(jù)的一致性被破壞共享數(shù)據(jù)的一致性被破壞n多線程同步處理的目的是為了讓多個線程協(xié)調(diào)地多線程同步處理的目的是為了讓多個線程協(xié)調(diào)地并發(fā)工作。并發(fā)工作。例8:由多個并發(fā)線程共享內(nèi)存引發(fā)的問題:加減法失敗1.public class ThreadSum extends Thread2. public static int data=0;3. public static int times=10000;4. public int ID;5. public boolean done;6. ThreadSum(int id)7. ID=id;8.
26、public void run( ) done=false; int d= (ID % 2=0) ? 1 : -1); System.out.println(運行線程運行線程: + ID + (增量為增量為: + d + ); for(int i=0; itimes; i+)2121 for(int j=0; jtimes; j+)2222 data+=d;2323 done=true;2424 System.out.println(結(jié)束線程結(jié)束線程: + ID);2525 / 方法方法run結(jié)束結(jié)束2626 2727 public static void main(String args )
27、2828 ThreadSum t1 = new ThreadSum(1);2929 ThreadSum t2 = new ThreadSum(2);3030 t1.done=false;3131 t2.done=false;3232 t1.start( );3333 t2.start( );3434 / 等待兩個線程運行結(jié)束等待兩個線程運行結(jié)束3535 while ( !t1.done | !t2.done ) 3636 ; 3737 System.out.println(結(jié)果結(jié)果: data= + data);3838 3939 加減法失敗程序分析n每次運行的結(jié)果不一致。每次運行的結(jié)果不一致
28、。nmain方法中創(chuàng)建了兩個線程方法中創(chuàng)建了兩個線程t1和和t2,t1.done和和t2.done是兩個不同的變量,占用不同的內(nèi)存空間,是兩個不同的變量,占用不同的內(nèi)存空間,而而data是是t1和和t2共享的內(nèi)存數(shù)據(jù)。共享的內(nèi)存數(shù)據(jù)。n線程線程t1對對data增加增加timestimes次,每次加次,每次加1,而而t2對對data減小減小timestimes次,每次減次,每次減1。若。若t1加加1的操作和的操作和t2減減1的操作都正確完成,則的操作都正確完成,則data的結(jié)果值為的結(jié)果值為0n但但data的值不為的值不為0,是因為兩個線程并發(fā)運行造,是因為兩個線程并發(fā)運行造成的,當出現(xiàn)兩個線程
29、同時要改變成的,當出現(xiàn)兩個線程同時要改變data值時,對值時,對data的值加的值加1或減或減1的操作就有可能無法完成。的操作就有可能無法完成。多線程同步的基本原理n多個并發(fā)線程之間共用資源,則需要進行同步處理多個并發(fā)線程之間共用資源,則需要進行同步處理對象鎖的概念對象鎖的概念n一個程序的各個并發(fā)線程中對同一個對象進行訪問的一個程序的各個并發(fā)線程中對同一個對象進行訪問的代碼段,成為臨界區(qū)。代碼段,成為臨界區(qū)。n在在java語言中,臨界區(qū)可以是一個語句塊或一個方法,語言中,臨界區(qū)可以是一個語句塊或一個方法,并且用并且用synchronized關鍵字標識。關鍵字標識。n臨界區(qū)的控制是通過對象鎖進行
30、的。臨界區(qū)的控制是通過對象鎖進行的。Java平臺將每平臺將每個由個由synchronized(someObject) 語句指定的對象語句指定的對象someObject設置一個鎖,稱為設置一個鎖,稱為對象鎖對象鎖。n對象鎖是一種獨占的排它鎖,即一個線程獲取對象的對象鎖是一種獨占的排它鎖,即一個線程獲取對象的鎖后,便擁有該對象的操作權(quán),其它任何線程不能對鎖后,便擁有該對象的操作權(quán),其它任何線程不能對該對象進行任何操作。該對象進行任何操作。同步方法和同步語句塊同步方法和同步語句塊n同步方法:用關鍵字同步方法:用關鍵字synchronized 修飾方法修飾方法n同步語句塊的定義格式如下同步語句塊的定義
31、格式如下:synchronized (引用類型的表達式引用類型的表達式) 語句塊語句塊方法方法wait/notify/notifyAlln方法方法wait:在其他線程調(diào)用此對象的在其他線程調(diào)用此對象的 notify() 方法或方法或 notifyAll() 方法前,導致當前線程等方法前,導致當前線程等待。待。n方法方法notify:喚醒在此對象監(jiān)視器上等待的單喚醒在此對象監(jiān)視器上等待的單個線程個線程n方法方法notifyAll:喚醒在此對象監(jiān)視器上等待的喚醒在此對象監(jiān)視器上等待的所有線程。所有線程。 線程死鎖的防治n如果程序中多個線程互相等待對方持有的鎖,如果程序中多個線程互相等待對方持有的鎖
32、,而在得到對方鎖之前都不會釋放自己的鎖,則而在得到對方鎖之前都不會釋放自己的鎖,則會導致這些線程不能繼續(xù)運行,這就是會導致這些線程不能繼續(xù)運行,這就是死鎖死鎖。nJava沒有檢測與避免死鎖的專門機制,完全沒有檢測與避免死鎖的專門機制,完全由程序進行控制由程序進行控制nJava防治死鎖的做法是:如果程序要訪問多防治死鎖的做法是:如果程序要訪問多個共享數(shù)據(jù),首先從全局考慮獲得鎖的順序,個共享數(shù)據(jù),首先從全局考慮獲得鎖的順序,并且在整個程序中都遵守這個順序;釋放鎖時,并且在整個程序中都遵守這個順序;釋放鎖時,要按加鎖的反序釋放。要按加鎖的反序釋放。例例9:生產(chǎn)者和消費者問題:生產(chǎn)者和消費者問題n生產(chǎn)
33、者和消費者模型是典型的線程同步問題,生產(chǎn)者和消費者模型是典型的線程同步問題,下面我們通過這個模型來說明線程同步的處理下面我們通過這個模型來說明線程同步的處理方法:方法:n使用某種資源的線程稱為消費者,產(chǎn)生或釋放使用某種資源的線程稱為消費者,產(chǎn)生或釋放這個資源的線程稱為生產(chǎn)者。這個資源的線程稱為生產(chǎn)者。1.生產(chǎn)者生成生產(chǎn)者生成10個整數(shù)(個整數(shù)(09),存儲到一個共享對),存儲到一個共享對象中,并把它們打印出來。象中,并把它們打印出來。2.每生成一個數(shù)就隨機休眠每生成一個數(shù)就隨機休眠0100毫秒,然后重復這毫秒,然后重復這個過程。個過程。3.一旦這一旦這10個數(shù)可以從共享對象中得到,消費者將盡可
34、個數(shù)可以從共享對象中得到,消費者將盡可能快地消費這能快地消費這10個數(shù),即把它們?nèi)〕龊蟠蛴〕鰜怼€數(shù),即把它們?nèi)〕龊蟠蛴〕鰜?。生產(chǎn)者程序生產(chǎn)者程序1.public class Producer extends Thread 2. private Share shared;3. private int number;4. public Producer(Share s, int number) 5. shared=s;6. this.number=number;7. 8. public void run( ) 9. for (int i=0; i10; i+) 10. shared.put(i)
35、;11. System.out.println(“生產(chǎn)者生產(chǎn)者”+this.number+“輸出的數(shù)據(jù)為:輸出的數(shù)據(jù)為:+ i);12. try 13. sleep(int)(Math.random( ) * 100);14. catch (InterruptedException e) 15. 16. 17.消費者程序消費者程序1.public class Consumer extends Thread 2. private Share shared;3. private int number;4. public Consumer(Share s, int number) 5. shared
36、=s;6. this.number=number;7. 8. public void run( ) 9. int value = 0;10. for (int i=0; i10; i+) 11. value=shared.get( );12. System.out.println(“消費者消費者”+this.number+“得到的數(shù)據(jù)為:得到的數(shù)據(jù)為: value);13. 14. 15.共享資源對象共享資源對象1.public class Share 2. private int contents;3. public int get( )4. return contents;5. 6. pu
37、blic void put(int value)7. contents=value;8. 9.測試的主程序測試的主程序1.public class PCTest 2.public static void main(String args) 3.Share s=new Share( );4.Producer p=new Producer(s,1);5.Consumer c=new Consumer(s,1);6.p.start( );7.c.start( );8. 9.運行結(jié)果結(jié)果分析n我們分析一下可能發(fā)生的情況:一種情況是生產(chǎn)者比我們分析一下可能發(fā)生的情況:一種情況是生產(chǎn)者比消費者速度快,那么
38、在消費者還沒有取出上一個數(shù)據(jù)消費者速度快,那么在消費者還沒有取出上一個數(shù)據(jù)之前,生產(chǎn)者又存入了新數(shù)據(jù),于是,消費者很可能之前,生產(chǎn)者又存入了新數(shù)據(jù),于是,消費者很可能會跳過上一個數(shù)據(jù)。另一種情況則相反,當消費者比會跳過上一個數(shù)據(jù)。另一種情況則相反,當消費者比生產(chǎn)者速度快,消費者可能兩次取出同一個數(shù)據(jù)。生產(chǎn)者速度快,消費者可能兩次取出同一個數(shù)據(jù)。n這兩種情況不是我們所希望的。我們希望生產(chǎn)者存入這兩種情況不是我們所希望的。我們希望生產(chǎn)者存入一個數(shù),消費者取出的就是這個數(shù)。為了避免上述情一個數(shù),消費者取出的就是這個數(shù)。為了避免上述情況發(fā)生,就必須鎖定生產(chǎn)者線程,當它向共享對象中況發(fā)生,就必須鎖定生產(chǎn)
39、者線程,當它向共享對象中存儲數(shù)據(jù)時禁止消費者線程從中取出數(shù)據(jù),反之也一存儲數(shù)據(jù)時禁止消費者線程從中取出數(shù)據(jù),反之也一樣。將共享對象樣。將共享對象Share中的中的put和和get分別定義為同步分別定義為同步化方法就可達到這個目的?;椒ň涂蛇_到這個目的。解決方法解決方法共享資源對象實現(xiàn)同步化共享資源對象實現(xiàn)同步化1.public class Share 2. private int contents;3. private boolean available=false;4. public synchronized int get( ) 5. while (available=false) 6.
40、 try 7. wait( );/線程等待并暫時釋放共享數(shù)據(jù)對象的鎖8. catch (InterruptedException e) 9. 10. available=false;11. notifyAll( );/釋放對象鎖,通知正在等待的線程重新占有鎖并運行12. return contents;13. 解決方法解決方法共享資源對象實現(xiàn)同步化共享資源對象實現(xiàn)同步化(續(xù)續(xù))14. public synchronized void put(int value) 15. while (available=true) 16. try 17. wait( );18. catch (Interrup
41、tedException e) 19. 20. contents=value;21. available=true;22. notifyAll( );23. 24.運行結(jié)果修改后的程序分析1n修改后的修改后的Share仍利用仍利用put和和get方法來寫入和讀取數(shù)據(jù),但增加方法來寫入和讀取數(shù)據(jù),但增加了了wait和和notifyAll功能。功能。nwait使線程進入短暫休眠,收到使線程進入短暫休眠,收到notifyAll的通知后會馬上醒來。的通知后會馬上醒來。n當消費者線程調(diào)用共享對象的當消費者線程調(diào)用共享對象的get方法時,如果生產(chǎn)者沒有寫入數(shù)方法時,如果生產(chǎn)者沒有寫入數(shù)據(jù),據(jù),availa
42、ble變量就會保持為假,線程進入循環(huán)并調(diào)用變量就會保持為假,線程進入循環(huán)并調(diào)用wait方法方法等待。等待。n一旦生產(chǎn)者寫入了新數(shù)據(jù),一旦生產(chǎn)者寫入了新數(shù)據(jù),available的值就會改變,同時生產(chǎn)者的值就會改變,同時生產(chǎn)者還會向消費者發(fā)出通知,喚醒消費者線程退出循環(huán)。還會向消費者發(fā)出通知,喚醒消費者線程退出循環(huán)。n此時,消費者線程將做兩個非常重要的工作,一是把此時,消費者線程將做兩個非常重要的工作,一是把available變變量改為假,二是通知生產(chǎn)者線程。最后,返回量改為假,二是通知生產(chǎn)者線程。最后,返回contents,它包含,它包含最新寫入的數(shù)據(jù)。最新寫入的數(shù)據(jù)。修改后的程序分析2n當生產(chǎn)
43、者線程第一次調(diào)用共享對象的當生產(chǎn)者線程第一次調(diào)用共享對象的put方法時,方法時,available變量為假,線程將跳過循環(huán)并將第一個數(shù)據(jù)變量為假,線程將跳過循環(huán)并將第一個數(shù)據(jù)寫入寫入contents變量,然后將變量,然后將available變量改為真值,變量改為真值,調(diào)用調(diào)用notifyAll方法通知消費者線程可以取數(shù)據(jù)了。方法通知消費者線程可以取數(shù)據(jù)了。n再次調(diào)用再次調(diào)用put方法時,如果消費者沒有取走數(shù)據(jù),方法時,如果消費者沒有取走數(shù)據(jù),available變量就會保持為真,線程將進入循環(huán)并調(diào)用變量就會保持為真,線程將進入循環(huán)并調(diào)用wait方法等待。方法等待。n一旦消費者取走上一個數(shù)據(jù),一旦
44、消費者取走上一個數(shù)據(jù),available的值就會改變,的值就會改變,線程也會被喚醒并退出循環(huán),繼續(xù)后面的工作。線程也會被喚醒并退出循環(huán),繼續(xù)后面的工作。n采用這樣的處理方式,就可以保證消費者一直等到生采用這樣的處理方式,就可以保證消費者一直等到生產(chǎn)者寫入一個新數(shù)據(jù)后再把它取出,而生產(chǎn)者則一直產(chǎn)者寫入一個新數(shù)據(jù)后再把它取出,而生產(chǎn)者則一直等到消費者取走上一個數(shù)據(jù)后再寫入新數(shù)據(jù)。等到消費者取走上一個數(shù)據(jù)后再寫入新數(shù)據(jù)。多線程問題多線程問題對多線程程序本身來說,它會對系統(tǒng)產(chǎn)生以下影響:對多線程程序本身來說,它會對系統(tǒng)產(chǎn)生以下影響:1)線程需要占用內(nèi)存。線程需要占用內(nèi)存。2)線程過多,會消耗大量線程過
45、多,會消耗大量CPU時間來跟蹤線程。時間來跟蹤線程。3)必須考慮多線程同時訪問共享資源的問題,如果沒必須考慮多線程同時訪問共享資源的問題,如果沒有協(xié)調(diào)好,就會產(chǎn)生令人意想不到的問題,例如可有協(xié)調(diào)好,就會產(chǎn)生令人意想不到的問題,例如可怕的死鎖和資源競爭。怕的死鎖和資源競爭。4)因為同一個任務的所有線程都共享相同的地址空間,因為同一個任務的所有線程都共享相同的地址空間,并共享任務的全局變量,所以程序也必須考慮多線并共享任務的全局變量,所以程序也必須考慮多線程同時訪問全局變量的問題。程同時訪問全局變量的問題。8.4 基于基于TCP的網(wǎng)絡程序設計的網(wǎng)絡程序設計n傳輸控制協(xié)議傳輸控制協(xié)議TCP (Tra
46、nsfer Control Protocol) 是一種基于連接的協(xié)議,可以在兩是一種基于連接的協(xié)議,可以在兩臺計算機之間提供可靠的數(shù)據(jù)傳輸。臺計算機之間提供可靠的數(shù)據(jù)傳輸。q基于連接的協(xié)議基于連接的協(xié)議q服務器端與客戶端通過服務器端與客戶端通過TCP協(xié)議進行通訊協(xié)議進行通訊qTCP, 反過來反過來, 運用了運用了IP協(xié)議協(xié)議qIP 協(xié)議只用來處理數(shù)據(jù)包協(xié)議只用來處理數(shù)據(jù)包TCP/IP 網(wǎng)絡模型網(wǎng)絡模型服務器端服務器端ServerSocket(port#)ServerSocket.accept()OutputStreamInputStreamSocket.close()客戶端客戶端Socket(
47、host, port#)OutputStreamInputStreamSocket.close()類類 .Socketn類類.Socket允許如下的四種基本操作允許如下的四種基本操作1. 連接到遠程的機器連接到遠程的機器2. 發(fā)送數(shù)據(jù)發(fā)送數(shù)據(jù)3. 接收數(shù)據(jù)接收數(shù)據(jù)4. 關閉連接關閉連接類類.Socket中的成員方法中的成員方法n構(gòu)造方法構(gòu)造方法ngetInputStream(): 返回該返回該socket所對應的輸所對應的輸入流入流ngetOutputStream():返回該返回該socket所對應的所對應的輸出流輸出流創(chuàng)建類創(chuàng)建類 Socket 的實例對象的實例對象n構(gòu)造方法構(gòu)造方法Sock
48、et() Socket(InetAddress address, int port)Socket(InetAddress address, int port, InetAddress localAddr, int localPort) Socket(String host, int port) Socket(String host, int port, InetAddress localAddr, int localPort)n示例示例: Socket javaSite = new Socket(, 80); 類類.ServerSocketServerSocket類的主要方法(1) Socke
49、t accept() throws IOException 等待客戶連接,該方法將阻塞當前系統(tǒng)服務線程,直到連接成功。該方法返回一個套接字類對象,通過該套接字,新的服務子線程與連接的客戶進行通信。(2) Void close() throws IOException 關閉套接字怎樣用socket進行客戶與服務器通信 Socket是兩個實體之間進行通信的有效端點。通過socket可以獲得源IP地址和源端口、終點IP地址和終點端口。用戶可以將多個socket連入同一個端口,以便對于單個端口可以有多個連接。 通過socket客戶/服務器編程可以創(chuàng)建一個能被許多人使用的分布式程序,并且所有客戶均可以用
50、統(tǒng)一的前端進行工作,并與服務器進行通信。 與服務器通信必須具備三個條件 n服務器程序n客戶程序n連接它們的socket程序 ServerSocket類類 n它的實例使服務器能夠檢測到指定端口的信息它的實例使服務器能夠檢測到指定端口的信息 naccept()方法可以使服務器檢測到指定端口的方法可以使服務器檢測到指定端口的活動活動 n服務器還負責檢測要求與它連接的客戶。服務器還負責檢測要求與它連接的客戶。 Socket類 ngetInputStream()和和getOutStream()方法來方法來發(fā)送和捕捉數(shù)據(jù)。發(fā)送和捕捉數(shù)據(jù)。 try /傳遞給它一個整數(shù)作為服務器指定可以使用的給定端口傳遞給它
51、一個整數(shù)作為服務器指定可以使用的給定端口 ServerSocket myServerSocket=new ServerSocket(80); Socket mySocket=myServerSocket.accept(); /檢測端口的活動檢測端口的活動 catch(Exception e) Accept()方法直到接收到用戶的連接請求,才繼續(xù)執(zhí)行中斷的執(zhí)行程序。一旦客戶的連接成功,mySocket就代表該連接,并且可以發(fā)送和接收數(shù)據(jù)。 最后,我們看一看客戶是怎樣請求連接的。其連接方法如下: try Socket mySocket=new Socket(,80); catch(Exceptio
52、n e ) Socket類 (一)TCP協(xié)議通信的服務器方實現(xiàn)(1) 假設服務器工作在端口8000上,ServerSocket svrsoc ServerSocket(8000)Socket socsvrsoc.accept();/監(jiān)視端口8000的連接請求(2) 打開與soc綁定的輸入/輸出流:In=new BufferedReader(new InputStreamRead(soc.getInputStream();/在套接字soc上綁定的輸入流基礎上構(gòu)造BufferedReader對象TCP協(xié)議通信的服務器方實現(xiàn)(續(xù))out=new PrintWrite(new bufferedWrit
53、e(new OutputStreamWrite(soc.getOutputStream(),true);/在套接字soc上綁定的輸出流基礎上構(gòu)造PrintWrite對象服務器使用in和out兩個實例從客戶接收輸入信息和向客戶程序發(fā)信息,同樣,在客戶端也應該建立這兩個對象,用來與服務器程序進行雙向通信。(一) TCP協(xié)議通信的服務器方實現(xiàn)(續(xù))(3) 獲取客戶機的IP地址,并在服務器窗口中顯示客戶機的地址信息:clientIP=soc.getInetAddress();System.out.println(“Clients IP address:”+clientIP);(4) 讀入一行客戶的輸入
54、,并回顯該行 String strin.readLine(); while(!str.equals(“quit”); System.out.println(“Client said:”+str); str=in.readLine(); (一) TCP協(xié)議通信的服務器方實現(xiàn)(續(xù))(5) 不斷循環(huán)以上代碼,直到客戶輸入“quit”為止。System.out.println(“Client want to leave.”);finall in.close(); out.close(); soc.close(); svrsoc.close();(二) TCP協(xié)議通信的客戶方實現(xiàn)(1)創(chuàng)建一個指向固定主
55、機的固定端口的Socket: Socket socnew Socket(“l(fā)ocalhost”,8000);(2) 從Socket對象中獲取與其綁定的輸入和輸出流In=new BufferedReader(new InputStreamRead(soc.getInputStream();out=new PrintWrite(new bufferedWrite(new OutputStreamWrite(soc.getOutputStream(),true);(二) TCP協(xié)議通信的客戶方實現(xiàn)(續(xù))(3)建立輸入/輸出流后,從服務器讀取發(fā)來的”welcome!”信息,顯示在窗口:Strin=in
56、.readLine();System.out.println(“Server said:”+strin);(二) TCP協(xié)議通信的客戶方實現(xiàn)(續(xù))(4)客戶向服務器發(fā)送的數(shù)據(jù)流從鍵盤獲取byte bmsg=new byte200;System.in.read(bmsg); /從鍵盤讀入bmsgString msg=new String(bmsg,0); /byte型轉(zhuǎn)String型Msg=msg.trim(); /刪除msg兩邊的空格(二) TCP協(xié)議通信的客戶方實現(xiàn)(續(xù))(5)當鍵盤輸入不是“quit”時,將鍵盤輸入的數(shù)據(jù)寫入輸出流中,并發(fā)送出去,然后繼續(xù)從鍵盤獲取輸入數(shù)據(jù),不斷循環(huán),直到輸
57、入“quit”時,先將其傳送給服務器,然后關閉輸入/輸出流和Socket:out.println(strout);In.close();out.close();soc.close();System.exit(0);一個簡單的客戶端/服務器程序1.import .*; 2.import java.io.*; 3.import java.lang.*; 4.public class myserver 5. public static void main(String args) 6. ServerSocket server; 7. Socket socket; 8. String s; 9. In
58、putStream Is; 10. OutputStream Os; 11. DataInputStream DIS; 12. PrintStream PS; 13. try 14. server=new ServerSocket(4321); 15. socket=server.accept();16. System.out.println(server ok); 17. System.out.println(*); 18. System.out.println(); 19. Is=socket.getInputStream(); 20. Os=socket.getOutputStream(
59、); 21. DIS=new DataInputStream(Is); 22. PS=new PrintStream(Os); 23. DataInputStream in=new DataInputStream(System.in); 24. while(true) 25. System.out.println(); 26. System.out.println(please wait clients message.); 27. System.out.println(); 28. s=DIS.readLine(); 29. System.out.println(client said:+s
60、); 30. if(s.trim().equals(BYE)break; 31. System.out.print(you say:); 32. s=in.readLine(); 33. PS.println(s); 34. if(s.trim().equals(BYE)break; 35. 36. DIS.close(); 37. PS.close(); 38. Is.close(); 39. Os.close(); 40. socket.close(); 41. catch(Exception e) 42. System.out.println(Error:+e); 43. /catch44. /
溫馨提示
- 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. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 年度預算與財務目標設定計劃
- 系列美術(shù)創(chuàng)作主題教案計劃
- 打破部門壁壘的協(xié)同計劃
- 電子政務:管理信息化整合之道
- 第五章+第1節(jié)《透鏡》教學設計 -2023-2024學年人教版物理八年級上學期
- 第二單元第9課《記錄校園生活》教學設計 2023-2024學年青島版(2019)初中信息技術(shù)第二冊
- 2025年山東貨運從業(yè)資格模擬考試題app
- 2025年太原貨運從業(yè)資格證考試題技巧
- 2025年鄭州貨運資格證考試真題
- 2025年高中化學必修二核心框圖和內(nèi)容結(jié)構(gòu)預復習
- 應付賬款主題分析小結(jié)
- SPC CPK超全EXCEL模板完整版可編輯
- 跬智信息(Kyligence):2023指標平臺建設方法與實踐白皮書
- 健康主題班會課件 正確使用手機
- 攝影構(gòu)圖技巧-完美人像攝影-攝影作品欣賞
- 行人過街調(diào)查表
- 弟子規(guī)全文及解釋精簡打印版
- 《畜牧獸醫(yī)行政法規(guī)》教案
- GJB9001C質(zhì)量手冊+程序文件+記錄清單
- 2023年安徽審計職業(yè)學院單招職業(yè)適應性測試題庫及答案解析
- JJG 875-2019數(shù)字壓力計
評論
0/150
提交評論