




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、Java多線程全磊磊u Java線程及進(jìn)程u Java中的線程類(Thread)u Java 中的 Runnable 接口u兩種實(shí)現(xiàn)多線程方式的對(duì)比分析u Java中線程的同步早期的 Winodw3.x下,進(jìn)程是最小運(yùn)行單位.在 Window95/NT下,每個(gè)進(jìn)程還可以啟動(dòng)幾個(gè)線程,比如每下載一個(gè)文件可以單獨(dú)開一個(gè)線程.在Windows 95/NT下,線程是最小單位.WINDOWS勺多任務(wù)特性使得線程之間獨(dú)立進(jìn)行,但是它們彼此 共享虛擬空間,也就是共用變量,線程有可能會(huì)同時(shí)操作一片內(nèi)存.n (1)線程的劃分尺度小于進(jìn)程,使得多線程程序的并發(fā)性高n (2)進(jìn)程在執(zhí)行過程中擁有獨(dú)立的內(nèi)存單元,而多
2、個(gè)線程共享內(nèi)存,從而極大地提高了程序的運(yùn)行效率.n (3)每個(gè)獨(dú)立的線程有一個(gè)程序運(yùn)行的入口,順序執(zhí)行序列和程序的出口但是線程不能獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線 程執(zhí)行控制.n 1. 虛擬的CPU由java.lang.Thread類圭寸裝和虛擬;n 2. CPU所執(zhí)行的代碼,傳遞給 Thread類對(duì)象;n 3. CPU所處理的數(shù)據(jù),傳遞給 Thread類對(duì)象。Java中的線程類(Thread)n Java的線程是通過 java.lang.Thread類來控制的,一個(gè)Thread類的對(duì)象代表一個(gè)線程,而且只能代表一個(gè)線程.通過 Thread類和它定義的對(duì)象,我 們可以獲得當(dāng)
3、前線程對(duì)象,獲取某一線程的名稱,可以實(shí)現(xiàn)控制線程暫停 一段時(shí)間等功能,n package com.yzy?n import java.sql.*?n class Threadl extends Thread n private int n?n public Thread1(int n) n this .n = n?n n public void run() n for (int i = 0? i < 100? i+) n System.out.println(” 第"+ n + "個(gè)線程的第"+ i + "次運(yùn)行"n n n n publ
4、ic class SimpleSwing n public static void main(String args) n Thread1 t1 = new Thread1(1)?n Thread1 t2 = new Thread1(2)?nt1.ru n()?單線程運(yùn)行nt2.ru n()?單線程運(yùn)行n System.out.println(” 主線程結(jié)束")?n n ,顯然這是一個(gè)單n程序執(zhí)行完t1.run(), 再執(zhí)行t2.run(),最后結(jié)束主線程線程程序.n 個(gè)代碼段被執(zhí)行,一定是在某個(gè)線程上運(yùn)行,代碼與線程密不可分,同一段代碼可以與多個(gè)線程相關(guān)聯(lián),在多個(gè)線程上執(zhí)行的也可以
5、是相同的一段代碼.n我們對(duì)上面的代碼進(jìn)行修改,讓Threadl繼承Thread,用Threadl的對(duì)象調(diào)用它繼承的方法 start(),修改代碼如下:n package com.yzy?n import java.sql.*?n class Thread1 extends Thread n private int n?n public Thread1(int n) n this .n = n?n n public void run() n for (int i = 0? i < 100? i+) n System.out.println(” 第"+ n + "個(gè)線程的
6、第"+ i + "次運(yùn)行")?n n n n public class SimpleSwing n public static void main(String args) n Thread1 t1 = new Thread1(1)?n Thread1 t2 = new Thread1(2)?n t1.start()?多線程運(yùn)行n t2.start()?多線程運(yùn)行n System.out.println(” 主線程結(jié)束")?n n n從結(jié)果可以看到:主線程先結(jié)束,然后兩個(gè)線程交替運(yùn)行.n 上面的代碼讓 Threadl類繼承了 Thread類,也就是Thr
7、eadl具備了 Thread 類的全部特點(diǎn),程序沒有直接調(diào)用Threadl類對(duì)象的run方法,而是調(diào)用了該類對(duì)象從Thread類繼承來的start方法.運(yùn)行后,能夠看到兩個(gè)for循環(huán)處的代碼同時(shí)交替運(yùn)行.n在單線程中,主線程必須等到Thread1.run函數(shù)返回后才能繼續(xù)往下執(zhí)行,而在多線程中,main()函數(shù)調(diào)用 Thread1.start() 方法啟動(dòng)了Thread1.run() 函數(shù)后,main 函數(shù) 不等待thread1.run 返回就繼續(xù)運(yùn) 行,Thread.run()函數(shù)在一邊獨(dú)自運(yùn)行,不影響原來的main()函數(shù)的運(yùn)行,這好比將一個(gè) 2G的CPU分成了兩個(gè)1G的CPU在一個(gè)CPU
8、上運(yùn)行main函 數(shù),而Thread1.run在另一個(gè)CPU上運(yùn)行.但他們都在向同一個(gè)顯示器上輸 出,所以我們看到兩個(gè) for循環(huán)處的代碼同時(shí)交替運(yùn)行.Java 中的 Runn able 接口n 在JDK文檔中,類Thread有一個(gè)構(gòu)造方法 Thread(Runnable target), 從JDK文檔中查看Runnable接口類的幫助,該接口中只有一個(gè) run方法,當(dāng)使 用Thread(Runnable target)方法創(chuàng)建線程對(duì)象時(shí),需為該方法傳遞一個(gè)實(shí)現(xiàn)了 Runn able接口的類對(duì)象,這樣創(chuàng)建的線程將調(diào)用那個(gè)實(shí)現(xiàn) Runnable接口的類對(duì)象中的run()方法為其運(yùn)行代碼,而不再調(diào)
9、用Thread類的run方法了.n package com.yzy?n class Threadl implements Runnable n private int n?n public Thread1(int n) n this .n = n?n n public void run() n for (int i = 0? i < 200? i+) n System.out.println("第"+ n + "個(gè)線程"n + Thread.currentThread ().toString() + "的第 ” + i + "次
10、運(yùn)行")?n n n 兩種實(shí)現(xiàn)多線程方式的對(duì)比分析那么這兩.我們用100n既然直接繼承 Thread類和實(shí)現(xiàn)Runnable接口都能實(shí)現(xiàn)多線程,種實(shí)現(xiàn)多線程方式在應(yīng)用上有什么區(qū)別呢n為了回答這個(gè)問題,我們通過編寫一個(gè)應(yīng)用程序,來進(jìn)行比較分析程序來模擬鐵路售票系統(tǒng),實(shí)現(xiàn)通過四個(gè)售票點(diǎn)發(fā)售某日某次列車的 張車票,一個(gè)售票點(diǎn)用一個(gè)線程來表示.n package com.yzy?n class ThreadTest extends Thread n private int tickets = 100?n public void run() nwhile (true) nif (tickets
11、 > 0) nSystem.out.pri ntl n( Thread.curre ntThread().getName()n+ " is sali ng ticket " + tickets-)?nn n n n public class ThreadDemo n public static void main( Stri ng args) nThreadTest t1 = new ThreadTest ()?nt1.start()?nt1.start()?nt1.start()?nt1.start()?方法n n在上面的代碼中,我們用ThreadTest類模擬售票
12、處的售票過程,run中的每一次循環(huán)總票數(shù)減1,模擬賣出一張車票,同時(shí)該車票號(hào)打印出來, 直到剩余的票數(shù)到零為止.在 ThreadDemo類的main方法中,我們創(chuàng)建了一 個(gè)線程對(duì)象,并重復(fù)啟動(dòng)四次,希望通過這種方式產(chǎn)生四個(gè)線程,但結(jié)果我 們發(fā)現(xiàn)其實(shí)只有一個(gè)線程在運(yùn)行,這個(gè)結(jié)果告訴我們:一個(gè)線程對(duì)象只能 啟動(dòng)一個(gè)線程,無論你調(diào)用多少遍start()方法,結(jié)果都只有一個(gè)線程.修改ThreadDem o在 ma in方法中創(chuàng)建四個(gè) ThreadTest對(duì)象n public class ThreadDemonpublic static void main(String args) nnewThread
13、1().start()?nnewThread1().start()?nnewThread1().start()?nnewThread1().start()?nnn從運(yùn)行結(jié)果我們看到的是票號(hào)被打印了四遍,即四個(gè)線程各自賣各自的100張票,而不是去賣共同的100張票.n我們創(chuàng)建了四個(gè)ThreadTest對(duì)象,就等于創(chuàng)建了四個(gè)資源,每個(gè) ThreadTest對(duì)象中都有100張票,每個(gè)線程在獨(dú)立地處理各自的資源.n經(jīng)過上面的實(shí)驗(yàn)和分析,可以總結(jié)出,要實(shí)現(xiàn)這個(gè)鐵路售票模擬程序,我們只能創(chuàng)建一個(gè)資源對(duì)象(該對(duì)象中包含要發(fā)售的那100張票),但要?jiǎng)?chuàng)建多 個(gè)線程去處理同一個(gè)資源對(duì)象,并且每個(gè)線程上所運(yùn)用的是相
14、同的程序代碼.n package com.yzy?n class Thread1 implements Runnable n private int tickets = 100?n public void run() n while (true ) n if (tickets > 0) n System.out.println(Thread. currentThread ().getName()n + " is sali ng ticket " + tickets-)?n n n n n public class SimpleSwing n public static
15、 void main(String args) n Thread1 t = new Thread1()?n new Thread(t).start()?n new Thread(t).start()?n new Thread(t).start()?n new Thread(t).start()?n n n上面的程序中,創(chuàng)建了四個(gè)線程,每個(gè)線程調(diào)用的是同一個(gè)ThreadTest 對(duì)象中的run()方法,訪問的是同一個(gè)對(duì)象中的變量(tickets)的實(shí)例,這個(gè)程序滿足了我們的需求.n實(shí)現(xiàn)Runnable接口相對(duì)于繼承了 Thread類來說,有如下好處:n 1)適合多個(gè)相同程序代碼的線程去處理同一資
16、源的情況,把虛擬CPU(線程)同程序的代碼,數(shù)據(jù)有效分離,較好地體現(xiàn)了面向?qū)ο蟮脑O(shè)計(jì)思想.n 2)可以避免由于Java的單繼承性帶來的局限.我們經(jīng)常遇到這樣的情況,即當(dāng)我們要將已經(jīng)繼承了某一個(gè)類的子類放入多線程中,由于一個(gè)類不能同時(shí)有兩個(gè)父類,所以不能用繼承Thread類的方式,那么,這個(gè)類只能采用實(shí)現(xiàn)Runnable接口的方式了 .n 3)有利于程序的健壯性,代碼能夠被多個(gè)線程共享,代碼與數(shù)據(jù)是獨(dú)立的當(dāng)多個(gè)線程的執(zhí)行代碼來自同一個(gè)類的實(shí)例時(shí),即稱它們共享相同的代碼 多個(gè)線程可以操作相同的數(shù)據(jù),與它們的代碼無關(guān)當(dāng)共享訪問的對(duì)象時(shí), 即它們共享相同的數(shù)據(jù)當(dāng)線程被構(gòu)造時(shí),需要的代碼和數(shù)據(jù)通過一個(gè)對(duì)
17、 象作為構(gòu)造函數(shù)實(shí)參傳遞進(jìn)去,這個(gè)對(duì)象就是一個(gè)實(shí)現(xiàn)了Runnable接口的類的實(shí)例n在上節(jié)車票的程序代碼中,極可能碰到一種意外,就是同一張票號(hào)被打印兩次或多次,也可能出現(xiàn)打印0甚至負(fù)數(shù)的票號(hào)這個(gè)意外發(fā)生在下面這部分代碼處:n if(tickets>0)nSystem.out.println(Thread.currentThread().getName()+saling ticket+tickets-)?n n假設(shè)tickets 的值為1的時(shí)候,線程1剛執(zhí)行完if(tickets>0)這行代碼,正準(zhǔn)備執(zhí)行下面的代碼,就在這時(shí),操作系統(tǒng)CPU又切回到了線程2上執(zhí)行,此時(shí)tickets的
18、值仍為1,線程2執(zhí)行完上面兩行代碼tickets 的值變?yōu)?后,CPU又切回到了線程1上執(zhí)行,線程1不會(huì)再執(zhí)行if(tickets>0) 這行 代碼,因?yàn)橄惹耙呀?jīng)比較過了,并且比較的結(jié)果為真,線程1將直接往下執(zhí) 行這行代碼:n System.out.println(Thread.currentThread().getName()+is”salingticket ” +tickets-)?n要想立即見到這種意外,可用在程序中調(diào)用Thread.sleep() 靜態(tài)方法來刻 意造成線程間的這種切換,Thread.sleep()方法迫使線程執(zhí)行到該處后暫 停執(zhí)行,讓出CPU給別的線程,在給定時(shí)間
19、(這里是毫秒)后,CPU回到剛才 暫停的線程上執(zhí)行.n class Threadl implements Runnable nprivate int tickets = 100?npublic void run() n while (true) n n n n n n n n n nif (tickets > 0) try Thread.sleep(10)? catch (Excepti on e) e.pri ntStackTrace()?System.out.pri ntl n( Thread.curre ntThread().getName()+ " is saling
20、ticket " + tickets-)?n public class SimpleSwing n public static void main( Stri ng args) n Thread1 t = new Thread1()?nnew Thread(t).start()?nnew Thread(t).start()?nnew Thread(t).start()?nnew Thread(t).start()?n n n在上面的程序代碼中,我們故意造成線程執(zhí)行完if(tickets>0) 語句后,執(zhí)行Thread.sleep(IO),以讓出 CPU給別的線程.同步代碼塊n如
21、何避免上面的這種意外,如何讓我們的程序是線程安全的呢?這就是我們要為大家講解的如何實(shí)現(xiàn)線程間的同步問題要解決上面的問題,我們必須保證下面這段代碼的原子性:n System.out.println(Thread.currentThread().getName()+” is salingticket ” +tickets-);n即當(dāng)一個(gè)線程運(yùn)行到if(tickets>0) 后,CPU不去執(zhí)行其他線程中的,可能影響當(dāng)前線程中的下句代碼的執(zhí)行結(jié)果的代碼塊,必須等到下一句執(zhí)行完 后才能去執(zhí)行其他線程中的有關(guān)代碼塊這段代碼就好比一座獨(dú)木橋,任 一時(shí)刻,只能有一個(gè)人在橋上行走,程序中不能有多個(gè)線程同時(shí)
22、在這兩句 代碼之間執(zhí)行,這就是線程同步.n在上面的代碼中,我們將這些需要具有原子性的代碼,放入synchronized語句內(nèi),形成了同步代碼塊在同一時(shí)刻只能有一個(gè)線程可以進(jìn)入同步代 碼塊內(nèi)運(yùn)行,只有當(dāng)該線程離開同步代碼塊后,其他線程才能進(jìn)入同步代 碼塊內(nèi)運(yùn)行.synchronized語句的格式為:n synchronized(object)代碼段"/object 可以是任意的一個(gè)對(duì)象n所以例子中用 private String str二new String(“” );隨便產(chǎn)生了一個(gè)對(duì)象,用在后面的同步代碼塊.n編譯運(yùn)行后,程序打印完最后一張票后,就停止了賣票的操作,這說明了改寫后的線
23、程是安全的n同步處理后,程序的運(yùn)行速度比原來沒有使用同步處理前更慢了,因?yàn)橄到y(tǒng)要不停地對(duì)同步監(jiān)視器進(jìn)行檢查,需要更多的開銷同步是以犧牲程序 的性能為代價(jià)的如果我們能夠確定程序沒有安全性的問題,就沒有必要 使用同步控制.n我們將程序代碼略作修改,改變String str二new String( “” )這行代碼的位置,將str對(duì)象放到run方法中定義:n class Thread1 implements Runnable n private int tickets = 100?n public void run() n String str = new String("")?
24、n while (true ) n synchronized (str) n if (tickets > 0) n try n Thread.sleep(10)?n catch (Exception e) n e.printStackTrace()?n n System.out.println(Thread. currentThread ().getName()n + " is sali ng ticket " + tickets-)?n n n n n n public class SimpleSwing n public static voidmain(Strin
25、g args) n Thread1 t = new Thread1()?n new Thread(t).start()?n new Thread(t).start()?n new Thread(t).start()?n new Thread(t).start()?n n n編譯運(yùn)行后,發(fā)現(xiàn)結(jié)果又不正常了,問題是run 方法被四個(gè)線程所調(diào)用,相當(dāng)于run方法被調(diào)用了四次,對(duì)每一次調(diào)用,程序都產(chǎn)生一個(gè)不同的str局部對(duì)象,這四個(gè)線程使用的同步監(jiān)視器完全是四個(gè)不同的對(duì)象,所以彼此之間不能同步同步函數(shù)n除了可以對(duì)代碼進(jìn)行同步外,也可以對(duì)函數(shù)實(shí)現(xiàn)同步,只要在需要同步的函數(shù)定義前加上syn chro ni
26、 zed 關(guān)鍵字即可.n package com.yzy?n class Threadl implements Runnable n private int tickets = 100?n public void run() n Stri ng str = new Stri ng("")?nwhile (true) nsale()?nn npublic syn chro ni zed void sale() if (tickets > 0) try Thread.sleep(10)? catch (Excepti on e) e.pri ntStackTrace()?
27、System.out.pri ntln (Thread.curre ntThread().getName()+ "is sali ng ticket" + tickets-)?public class SimpleSwi ng public static void main( Stri ng args) n Threadl t = new Thread1()?nnew Thread(t).start()?nnew Thread(t).start()?nnew Thread(t).start()?nnew Thread(t).start()?n n n編譯運(yùn)行后的結(jié)果同上面同
28、步代碼塊方式的運(yùn)行結(jié)果完全一樣,可見,在函數(shù)定義前使用syn chro nized關(guān)鍵字也能夠很好地實(shí)現(xiàn)線程間的同步.n在同一類中,使用synchronized關(guān)鍵字定義的若干方法,可以在多個(gè)線程之間同步,當(dāng)有一個(gè)線程進(jìn)入了 synchronized 修飾的方法(獲得監(jiān)視器), 其他線程就不能進(jìn)入同一個(gè)對(duì)象的所有使用了synchronized修飾的方法,直到第一個(gè)線程執(zhí)行完它所進(jìn)入的synchronized修飾的方法為止(離開監(jiān)視器).代碼塊與函數(shù)間的同步n掌握了同步代碼塊與同步函數(shù)兩種方式,能否在代碼塊與函數(shù)之間實(shí)現(xiàn)同步呢?線程同步靠的是檢查同一對(duì)象的標(biāo)志,只要讓代碼塊與函數(shù)使用同一個(gè)監(jiān)視器對(duì)象,就能實(shí)現(xiàn)代碼塊與函數(shù)同步n public class Demo n public static voidmain(String args) n Thread1 t = new Thread1()?n new Thread(t).start()
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 《意識(shí)障礙患者的護(hù)理》課件
- 關(guān)務(wù)危機(jī)管理培訓(xùn)體系構(gòu)建
- 飛機(jī)動(dòng)力系統(tǒng)設(shè)計(jì)試題及答案
- 《模具制造簡介》課件
- 《金融機(jī)構(gòu)信息系統(tǒng)管理》課件
- 《酒店管理專業(yè)導(dǎo)論》課件
- 《深入探討奶牛養(yǎng)殖管理》課件
- 《銷售人員核心素質(zhì)》課件
- 《人生追求與成功》課件
- 《汽車構(gòu)造與原理》課件介紹
- 【CMMI】8. TS、PI、VV、PR 訪談問題-編碼和測試 -(含答案)
- 06J403-1 樓梯、欄桿、欄板圖集
- 教科版六年級(jí)科學(xué)下冊(cè)全冊(cè)課件(2024年春季版)
- 提香-西方美術(shù)史-
- 歷年撫恤金表傷殘軍人撫恤金
- 明亞保險(xiǎn)經(jīng)紀(jì)人考試題庫答案
- 干部選拔任用程序
- 機(jī)械制造技術(shù)-機(jī)械加工工藝
- 供貨組織措施及供貨方案
- 商譽(yù)聲明文件
- 健身房運(yùn)營總體策劃方案
評(píng)論
0/150
提交評(píng)論