版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第十一講多線程線程基礎(chǔ)Java語言的線程Java線程的生存期★★鎖與線程同步★★★線程阻塞★★★什么是線程?
Definition1:Athreadisasinglesequentialflowofcontrolwithinaprogram.
Definition2:AbasicunitofCPUutilization,andconsistsofaprogramcounter,aregisterset,andstackspace.Itshareswithpeerthreadsitscodesection,datasection,andoperatingsystemresources.11.1線程基礎(chǔ)多線程vs
處理器
可由多個(gè)硬件處理器同時(shí)支持多線程。
也可由單個(gè)硬件處理器以時(shí)間分片方式支持多線程。
還可由多個(gè)硬件處理器以時(shí)間分片方式支持多線程。使用多線程的理由
①多線程可滿足問題域本質(zhì)上的并發(fā)性需求
并發(fā)問題最好以并發(fā)方式處理。
②可充分利用多處理器體系結(jié)構(gòu)以提高應(yīng)用程序性能
每一線程可在不同處理器上并發(fā)執(zhí)行。
③更方便地實(shí)現(xiàn)計(jì)算任務(wù)之間的資源共享
同一進(jìn)程中的各線程共享該進(jìn)程的內(nèi)存與其他資源。
④提高交互式應(yīng)用程序的響應(yīng)性(responsiveness)
多線程交互程序執(zhí)行長操作時(shí)可繼續(xù)響應(yīng)用戶的請(qǐng)求。
⑤比使用進(jìn)程更合算
線程上下文切換較之進(jìn)程的開銷更低(不必分配內(nèi)存或資源)。
線程之間的通信也比進(jìn)程間通信更有效(簡單且高效)。
⑥在分布式計(jì)算中服務(wù)程序通常采用多線程
從而提高服務(wù)程序的性能。為什么使用多線程?AllnontrivialapplicationsfortheJavaplatformaremultithreaded,whetheryoulikeitornot.使用多線程的代價(jià)
①多線程應(yīng)用程序遠(yuǎn)比單線程應(yīng)用程序復(fù)雜
程序員還需仔細(xì)處理多個(gè)計(jì)算任務(wù)之間的通信與同步。
②多線程應(yīng)用程序有更多的設(shè)計(jì)要求
并發(fā)性本質(zhì)上就有相當(dāng)?shù)膹?fù)雜性:同步、死鎖、公平(饑餓)、...
③多線程應(yīng)用程序更加難以調(diào)試和測試
應(yīng)用程序的不確定性導(dǎo)致這一困難。
④多線程應(yīng)用程序更難于實(shí)現(xiàn)
需掌握某一特定線程庫的具體用法。
⑤多線程應(yīng)用程序的行為通常與特定平臺(tái)有關(guān)
導(dǎo)致應(yīng)用程序的平臺(tái)可移植性降低。進(jìn)程──重量級(jí)任務(wù)
①每一進(jìn)程占用獨(dú)立的地址空間。
此處的地址空間包括代碼、數(shù)據(jù)及其他資源。
②進(jìn)程間的通信開銷較大且受到許多限制。
對(duì)象(或函數(shù))接口、通信協(xié)議、...
③進(jìn)程間的切換開銷也較大。
又稱上下文切換(contextswitch)。
上下文包括代碼、數(shù)據(jù)、堆棧、處理器狀態(tài)、資源、...線程──輕量級(jí)任務(wù)
①多個(gè)線程共享進(jìn)程的地址空間(代碼、數(shù)據(jù)、其他資源等)。
線程也需要自己的資源,如程序計(jì)數(shù)器、寄存器組、調(diào)用棧等。
②線程間的通信開銷較少且比較簡單。
因?yàn)楣蚕矶鴾p少了需要通信的內(nèi)容。
但也因?yàn)槌浞止蚕矶鵁o法對(duì)共享資源進(jìn)行保護(hù)。
③線程間的切換開銷也較小。
只需保存每一線程的程序計(jì)數(shù)器、寄存器組、堆棧等空間。
不必切換或復(fù)制整個(gè)地址空間,從而成本大為降低(約1/10)。線程vs
進(jìn)程Java線程機(jī)制建立在宿主操作系統(tǒng)的線程基礎(chǔ)上
將宿主操作系統(tǒng)的線程機(jī)制包裝為語言一級(jí)的機(jī)制提供給程序員使用。
對(duì)上:為程序員提供簡單一致、獨(dú)立于平臺(tái)的多線程編程接口。
對(duì)下:為程序員屏蔽宿主操作系統(tǒng)線程機(jī)制的細(xì)節(jié)。
不同宿主操作系統(tǒng)的線程機(jī)制有差別。
Solaris平臺(tái)上:
線程要么執(zhí)行結(jié)束,要么被更高優(yōu)先級(jí)線程搶占(pre-empt)。
Windows98/NT平臺(tái):
相同優(yōu)先級(jí)的線程以相同時(shí)間片段(quantum)輪流運(yùn)行。
這一機(jī)制又稱時(shí)間分片(timeslicing)。
與Solaris類似,也支持高優(yōu)先級(jí)線程的搶占。Java的線程機(jī)制Java線程的實(shí)現(xiàn)
將Java平臺(tái)的線程機(jī)制映射到本地的線程庫實(shí)現(xiàn)。
Java應(yīng)用程序員不必關(guān)心如何映射到宿主操作系統(tǒng)的線程庫。
這一任務(wù)是由JVM供應(yīng)商完成的。
Java應(yīng)用程序也可通過JNI訪問本地線程庫。
但絕大多數(shù)情況下都不主張程序員這樣做。Java線程內(nèi)核層線程用戶層線程線程vsJVM
如果應(yīng)用程序中僅有一個(gè)線程,程序員無需顯式地處理線程。
由JVM自動(dòng)完成對(duì)單線程的管理。
正如之前的大多數(shù)Java應(yīng)用程序所示。
JVM本身是一個(gè)進(jìn)程。
一個(gè)JVM可同時(shí)支持多個(gè)線程。
JVM本身總是多線程的。
總存在一些守護(hù)線程(daemonthread)。
例如:垃圾收集線程、鼠標(biāo)與鍵盤事件分派線程、...。
每一個(gè)JVM有一個(gè)堆;每一個(gè)線程有一個(gè)棧。
從而一個(gè)JVM中的所有線程可通過共享的棧交換信息。使用以下Java接口或類
接口java.lang.Runnable
類java.lang.Thread11.2Java語言的線程Step1:編寫一個(gè)線程類
程序員自定義的類:
①要么實(shí)現(xiàn)java.lang.Runnable接口。
更好的面向?qū)ο笤O(shè)計(jì),提供一致的設(shè)計(jì)風(fēng)格。
可節(jié)約Java語言寶貴的單繼承指標(biāo)。
例如,一個(gè)Applet程序必須選擇這一途徑實(shí)現(xiàn)多線程。
②要么繼承java.lang.Thread類。
該類實(shí)現(xiàn)了Runnable接口,但run()被定義為空的。
可用更簡單的代碼完成同樣的事情。
線程類中最重要的重定義方法:run()。
通過重定義該方法完成線程本身要執(zhí)行的任務(wù)。創(chuàng)建線程Step2:創(chuàng)建線程類的一個(gè)對(duì)象實(shí)例
①如果線程類實(shí)現(xiàn)Runnable接口:
首先,創(chuàng)建該線程類的一個(gè)實(shí)例。
然后,再以上述實(shí)例為參數(shù)創(chuàng)建Thread的一個(gè)實(shí)例。
②如果線程類繼承Thread類:
直接創(chuàng)建線程類的一個(gè)實(shí)例。
Thread類的構(gòu)造方法有3個(gè)參數(shù),共7種組合:
①一個(gè)Runnable對(duì)象實(shí)例
該對(duì)象的run()方法將在線程中執(zhí)行。
②一個(gè)String對(duì)象實(shí)例
用于標(biāo)識(shí)線程。
③一個(gè)ThreadGroup對(duì)象實(shí)例
將線程指派到某一線程組;線程組將相關(guān)線程組織在一起。Step3:調(diào)用對(duì)象實(shí)例的start()方法
該方法創(chuàng)建并運(yùn)行線程。
申請(qǐng)線程運(yùn)行所需系統(tǒng)資源、調(diào)度線程、調(diào)用線程的run()方法。
當(dāng)該方法返回時(shí),線程已開始運(yùn)行并處于Runnable狀態(tài)。
由于多數(shù)計(jì)算機(jī)只有一個(gè)處理器,JVM必須調(diào)度線程。packagejava.lang;public
interfaceRunnable{
public
abstract
voidrun();}//通過實(shí)現(xiàn)Runnable接口定義新線程classCounterimplementsRunnable{
inti;
public
voidrun(){ i=0;
while(true){ System.out.println("計(jì)數(shù)器="+i++);
if(i==100)break; } }}public
classRunnableThread{
public
static
voidmain(String[]args){ Countercounter=newCounter(); Threadthread=newThread(counter); thread.start(); System.out.println("主程序結(jié)束。"); }}例子程序一實(shí)現(xiàn)Runnable接口packagejava.lang;public
classThreadimplementsRunnable{ ...;
public
voidrun(){
if(target!=null)target.run(); } ...}//通過繼承Thread類定義新線程public
classSubclassThreadextendsThread{
public
voidrun(){
while(true){ //執(zhí)行線程自身的任務(wù)
try{ sleep(1000);
break; }catch(InterruptedExceptionexc){ //睡眠被中斷
} } }
public
static
voidmain(String[]args){ Threadt=newSubclassThread(); t.start(); System.out.println("主程序結(jié)束。"); }}例子程序二繼承Thread類classCountThreadextendsThread{
private
intfrom;
private
intto;
publicCountThread(intfrom,intto){
this.from=from;
this.to=to; }
public
voidrun(){ System.out.println(this.getName()+"啟動(dòng)
...");
for(intindex=from;index<=to;index++){ System.out.print(index+""); } System.out.println(this.getName()+"停止。"); }}public
classTwoThreads{
static
public
voidmain(String[]args){ CountThreadthread1=newCountThread(1,10); CountThreadthread2=newCountThread(20,30); thread1.start(); thread2.start(); }}創(chuàng)建兩個(gè)新的線程程序輸出有不確定性如果計(jì)數(shù)量少,好象是兩個(gè)線程依次執(zhí)行放大看(成千上萬),它們卻是交錯(cuò)執(zhí)行Thread定義了getName()和setName()主線程的特點(diǎn)
當(dāng)Java程序啟動(dòng)時(shí),一個(gè)主線程自動(dòng)地被創(chuàng)建并運(yùn)行。
主線程是產(chǎn)生應(yīng)用程序所有其他線程的線程。
主線程通常最后執(zhí)行完畢,以完成各種收尾任務(wù)。
如何獲取與使用主線程?
調(diào)用Thread類的類方法currentThread()。
一旦獲得主線程對(duì)象實(shí)例后,其操作與其他線程無異。
默認(rèn)情況下,主線程名為main,優(yōu)先級(jí)為5,非監(jiān)控線程。
main也同時(shí)是一個(gè)線程組的名字。主線程//獲取主線程并調(diào)用主線程的方法public
classMainThread{
public
static
voidmain(String[]args){ Threadthread=Thread.currentThread(); System.out.println("線程信息:"+thread); System.out.println("線程名字:"+thread.getName());
thread.setName("MyThread"); System.out.println("新的名字:"+thread.getName());
try{
for(intindex=1;index<=10;index++){ System.out.print(index);
thread.sleep(1000); } }catch(InterruptedExceptionexc){ System.out.println("main()被中斷!"); } }}Threadinfo:Thread[main,5,main]Name:mainNewname:MyThread12345678910(線程名字,優(yōu)先級(jí),線程組)//一個(gè)貪婪的線程,可能獨(dú)占所有CPU時(shí)間public
classGreedyThreadextendsThread{
intnumber;
publicGreedyThread(intnumber){
this.number=number; }
public
voidrun(){
for(intindex=1;index<=100000;index++){
if((index%10000)==0){ System.out.println("線程#"+number+":"+index); } } }
static
public
voidmain(String[]args){
newGreedyThread(1).start();
newGreedyThread(2).start(); }}這種在run()方法定義大循環(huán)的線程又稱自私(selfish)線程事件處理的線程
事件處理代碼在一個(gè)單獨(dú)線程(event-dispatchingthread)中執(zhí)行。
從而保證事件處理程序可在下一處理程序開始之前執(zhí)行完畢。
組件繪制代碼也在event-dispatchingthread中執(zhí)行。
這意味著執(zhí)行事件處理代碼時(shí),GUI被凍結(jié)。
即既不會(huì)重新繪制,也不會(huì)響應(yīng)鼠標(biāo)或鍵盤輸入。
事件處理程序設(shè)計(jì)原則:
事件處理代碼應(yīng)能盡快地執(zhí)行完畢,
否則你的程序感覺起來性能很差!
如果事件處理時(shí)真的需要執(zhí)行長時(shí)間操作,
啟動(dòng)另一線程或?qū)⒄?qǐng)求轉(zhuǎn)發(fā)給另一線程。Swing的事件處理模型importjavax.swing.*;importjava.awt.event.*;publicclassFreezeEDTextendsJFrameimplementsActionListener{privateJButtonfreezer;publicFreezeEDT(){super("Freeze");freezer=newJButton("Freeze");freezer.addActionListener(this);add(freezer);pack();}publicvoidactionPerformed(ActionEvente){try{Thread.sleep(4000);}catch(InterruptedExceptionevt){}}publicstaticvoidmain(String...args){SwingUtilities.invokeLater(newRunnable(){publicvoidrun(){FreezeEDTedt=newFreezeEDT();edt.setVisible(true);}});}}importjavax.swing.*;importjava.awt.event.*;publicclassFreezeEDTextendsJFrameimplementsActionListener{privateJButtonfreezer;publicFreezeEDT(){super("Freeze");freezer=newJButton("Freeze");freezer.addActionListener(this);add(freezer);pack();}publicvoidactionPerformed(ActionEvente){newThread(newRunnable(){publicvoidrun(){try{Thread.sleep(4000);}catch(InterruptedExceptionevt){}finalStringhot="Hot";SwingUtilities.invokeLater(newRunnable(){publicvoidrun(){freezer.setText(hot);}});}}).start();}
publicstaticvoidmain(String...args){SwingUtilities.invokeLater(newRunnable(){publicvoidrun(){FreezeEDTedt=newFreezeEDT();edt.setVisible(true);}});}}publicclassSellTicketsTester{publicstaticvoidmain(String[]args){SellTicketst=newSellTickets();//新建一個(gè)售票類的對(duì)象
newThread(t).start();//用此對(duì)象作為參數(shù)創(chuàng)建3個(gè)新線程并啟動(dòng)
newThread(t).start();newThread(t).start();}}classSellTicketsimplementsRunnable{privateinttickets=200;//將共享的資源作為私有變量
publicvoidrun(){while(tickets>0)//直到?jīng)]有票可售為止
{System.out.println(Thread.currentThread().getName()+"issellingticket"+tickets--);}}}線程間的數(shù)據(jù)共享線程狀態(tài)轉(zhuǎn)換略圖11.3Java線程的生存期interrupt()interrupt()yield()orI/OorI/O結(jié)束線程狀態(tài)轉(zhuǎn)換詳圖線程的狀態(tài)
New:線程對(duì)象實(shí)例已創(chuàng)建,但尚未調(diào)用該實(shí)例的start()方法。
此時(shí)線程尚未獲取運(yùn)行所需的任何系統(tǒng)資源。
Runnable:線程已調(diào)用了start()方法,可以被調(diào)度執(zhí)行。
此時(shí)線程已獲得運(yùn)行時(shí)所需的系統(tǒng)資源。
注意此時(shí)線程并不是正在執(zhí)行,僅是具備被調(diào)度執(zhí)行條件而已。
Running:線程被JVM的線程調(diào)度程序分配了CPU執(zhí)行時(shí)間。
線程正在執(zhí)行,隨時(shí)可能由JVM線程調(diào)度程序送回Runnable狀態(tài)。
Dead:線程被JVM的線程調(diào)度程序分配了CPU執(zhí)行時(shí)間。
處于Dead狀態(tài)的線程不會(huì)再被調(diào)度執(zhí)行。
Blocked:正在運(yùn)行的線程(Running狀態(tài))因各種原因被阻塞。
例如,線程進(jìn)入synchronized代碼段時(shí)未能申請(qǐng)到想要的鎖。
例如,線程調(diào)用wait()等待另一線程同步。
例如,線程調(diào)用sleep()進(jìn)入睡眠狀態(tài)。
例如,線程調(diào)用join()等待另一線程執(zhí)行完畢。
所有情況的阻塞都有相應(yīng)的事件使線程回到Running狀態(tài)。線程狀態(tài)轉(zhuǎn)換在某狀態(tài)執(zhí)行不合法的操作均導(dǎo)致IllegalThreadStateException非阻塞的狀態(tài)轉(zhuǎn)換
NewRunnable:調(diào)用線程的start()方法。
這標(biāo)志著一個(gè)線程的整個(gè)生存期的開始。
RunnableRunning:由JVM的線程調(diào)度程序負(fù)責(zé)管理。
注意這一雙向狀態(tài)轉(zhuǎn)換主要由調(diào)度程序負(fù)責(zé),而不是應(yīng)用程序。
RunningDead:線程的run()方法執(zhí)行完畢或異常返回。
這標(biāo)志著一個(gè)線程的整個(gè)生存期的結(jié)束。
不主張使用線程實(shí)例的stop()方法顯式終止線程執(zhí)行。yield()方法
應(yīng)用程序也可簡單地參與線程調(diào)度:
調(diào)用Thread類yield()方法暫停線程,從而允許其他線程執(zhí)行。
此時(shí)線程切換為由執(zhí)行狀態(tài)轉(zhuǎn)換為可執(zhí)行狀態(tài)。
等待JVM線程調(diào)度程序再次將線程轉(zhuǎn)換為運(yùn)行狀態(tài)。
Java線程機(jī)制并不保證優(yōu)先級(jí)高的線程搶占CPU時(shí)間。
例如,工作在Solaris或MacOS等非搶占型平臺(tái)上的綠色線程。
使用yield()方法有助于提高應(yīng)用程序的可移植性。//一個(gè)貪婪的線程,可能獨(dú)占所有CPU時(shí)間public
classGreedyThreadextendsThread{
intnumber;
publicGreedyThread(intnumber){
this.number=number; }
public
voidrun(){
for(intindex=1;index<=100000;index++){
if((index%10000)==0){ System.out.println("線程#"+number+":"+index); } } }
static
public
voidmain(String[]args){
newGreedyThread(1).start();
newGreedyThread(2).start(); }}這種在run()方法定義大循環(huán)的線程又稱自私(selfish)線程>javaGreedyThread線程#1:10000線程#1:20000線程#1:30000線程#1:40000線程#1:50000線程#1:60000線程#1:70000線程#1:80000線程#1:90000線程#1:100000線程#2:10000線程#2:20000線程#2:30000線程#2:40000線程#2:50000線程#2:60000線程#2:70000線程#2:80000線程#2:90000線程#2:100000//一個(gè)禮貌的線程,經(jīng)常將CPU機(jī)會(huì)讓給其他線程public
classPoliteThreadextendsThread{
intnumber;
publicPoliteThread(intnumber){
this.number=number; }
public
voidrun(){
for(intindex=1;index<=100000;index++){
if((index%10000)==0){ System.out.println("線程#"+number+":"+index); yield(); } } }
static
public
voidmain(String[]args){
newPoliteThread(1).start();
newPoliteThread(2).start(); }}程序員應(yīng)盡量編寫這一類有禮貌的線程在run()方法循環(huán)體中主動(dòng)調(diào)用yield()方法>javaPoliteThread線程#1:10000線程#2:10000線程#1:20000線程#2:20000線程#1:30000線程#2:30000線程#1:40000線程#2:40000線程#1:50000線程#2:50000線程#1:60000線程#2:60000線程#1:70000線程#2:70000線程#1:80000線程#2:80000線程#1:90000線程#2:90000線程#1:100000線程#2:100000共享資源的訪問
當(dāng)兩個(gè)線程對(duì)同一共享資源并發(fā)訪問時(shí)可能帶來問題。
特別是當(dāng)一個(gè)或多個(gè)線程對(duì)資源進(jìn)行“寫”操作時(shí)。
多線程應(yīng)用程序的復(fù)雜性主要體現(xiàn)在對(duì)共享資源訪問的處理。
例如,以下兩個(gè)任務(wù)以不同交錯(cuò)次序執(zhí)行時(shí)最終帳戶余額是相同的嗎?
TellerA
TellerB
Getsbalance Getsbalance
Adds$100 Subtracts$100
Storesbalance Storesbalance11.4鎖與線程同步交錯(cuò)執(zhí)行的單位不是程序中的語句而是字節(jié)碼的語句對(duì)象的鎖標(biāo)志
每一個(gè)對(duì)象實(shí)例都有一個(gè)標(biāo)志。
該標(biāo)志可理解為一個(gè)鎖。
每一對(duì)象實(shí)例都只有唯一的一個(gè)鎖。
類似對(duì)象實(shí)例,每一類對(duì)象也有一個(gè)鎖。
保留字synchronized允許與鎖標(biāo)志進(jìn)行交互!
在進(jìn)入synchronized代碼段時(shí)必須獲取鎖(上鎖)。
synchronized代碼段執(zhí)行完畢或異常中斷時(shí)歸還鎖(解鎖)。
鎖用于控制臨界區(qū)的訪問。
即保證特定代碼段的互斥。
鎖保證了線程是否有權(quán)限進(jìn)入某一特定代碼段。對(duì)象實(shí)例的鎖//一個(gè)例子public
classMyStack{
inttop=-1;
char[]data=new
char[128];
public
voidpush(charelement){ top++; data[top]=element; }
public
charpop(){
charelement=data[top]; top--;
returnelement; }}當(dāng)兩個(gè)線程調(diào)用MyStack的同一實(shí)例的方法時(shí)會(huì)產(chǎn)生問題this對(duì)象未上鎖代碼(行為)數(shù)據(jù)(狀態(tài))public
voidpush(charelement){ //線程試圖執(zhí)行synchronized(this)時(shí)
//this對(duì)象未上鎖,則執(zhí)行后上鎖
synchronized(this){ top++; data[top]=element; } //synchronized塊執(zhí)行完后解鎖}this對(duì)象已上鎖代碼(行為)數(shù)據(jù)(狀態(tài))public
charpop(){ //線程試圖執(zhí)行synchronized(this)時(shí)
//this對(duì)象已上鎖,則須等待解鎖
synchronized(this){
charelement=data[top]; top--;
returnelement; }}另一種譬喻是獲取鎖即上鎖相當(dāng)于鎖消失以下兩段代碼的作用相同public
voidpush(charc){
synchronized(this){ ... }}public
synchronized
voidpush(charc){ ...}指定獲取哪一個(gè)對(duì)象實(shí)例的鎖;也可以是一個(gè)類名字,表示該類的鎖Java的鎖是可再入的
如果不支持可再入,則如下例子將導(dǎo)致死鎖!public
classReentrant{
public
synchronized
voida(){ b(); System.out.println("在a()中調(diào)用..."); }
public
synchronized
voidb(){ System.out.println("在b()中調(diào)用..."); }
public
static
voidmain(String[]args){
newReentrant().a(); }}類似對(duì)象實(shí)例,每一“類對(duì)象”也有一個(gè)鎖
故可利用synchronized修飾類方法以實(shí)現(xiàn)對(duì)類變量的同步訪問。public
classSynchronizedValue{
intx;
public
voidincrement(){
synchronized(SynchronizedValue.class){ x++; } }
public
voiddecrement(){
synchronized(SynchronizedValue.class){ x--; } }}線程設(shè)計(jì)的基本原則
①所有對(duì)敏感數(shù)據(jù)的訪問都應(yīng)使用synchronized。
如果所有方法對(duì)數(shù)據(jù)只讀,則該數(shù)據(jù)通常不是敏感的。
②由synchronized保護(hù)的敏感數(shù)據(jù)的訪問控制應(yīng)定義為private的。
從而數(shù)據(jù)只能由該類的方法直接訪問。
從而我們只需對(duì)該類的方法提供一種同步機(jī)制。
③同步粒度的設(shè)計(jì)不宜太大。
一個(gè)同步單元(粒度)太大會(huì)影響程序執(zhí)行效率。
通常將那些真正需要訪問共享數(shù)據(jù)的代碼才放在同步代碼段中。線程同步設(shè)計(jì)設(shè)計(jì)正確的多線程應(yīng)用程序
①標(biāo)識(shí)可能由多個(gè)線程同時(shí)訪問的共享數(shù)據(jù)
這可能是設(shè)計(jì)多線程應(yīng)用程序的最重要事情。
這些數(shù)據(jù)通常定義了應(yīng)用程序的臨界區(qū)(criticalregion)。
②保證共享數(shù)據(jù)不被多個(gè)線程同時(shí)訪問
須保證訪問共享數(shù)據(jù)的各線程之間互斥(mutualexclusion)。
如果未正確處理互斥,可能導(dǎo)致不見預(yù)見的結(jié)果。
互斥通常利用數(shù)據(jù)上的鎖來實(shí)現(xiàn)。
鎖用于避免同時(shí)的并發(fā)訪問。//一個(gè)線程安全的程序public
classThreadSafeTeller{
privateintbalance=0;
privatebooleansmile=false;
synchronized
voidcredit(intcash){ balance+=cash; smile=(balance>10000); }
synchronized
voiddebit(intcash){ balance-=cash; smile=(balance>10000); }}sleep()方法join()方法wait()/notify()方法11.5線程阻塞sleep()方法
指定在某一段時(shí)間(以毫秒為單位)內(nèi)線程處于阻塞狀態(tài)。
狀態(tài)轉(zhuǎn)換:
RunningBlocked:調(diào)用一個(gè)線程的sleep()方法。
BlockedRunning:時(shí)間事件。
經(jīng)過指定時(shí)間段后,線程又自動(dòng)恢復(fù)為可執(zhí)行狀態(tài)。
JDK只保證在指定時(shí)間之后被喚醒,不保證準(zhǔn)確的時(shí)間!
注意事項(xiàng):
如果在同步代碼段中執(zhí)行本方法,線程占有的鎖不會(huì)釋放。
這種行為不同于下述的wait()方法。
應(yīng)用實(shí)例:
定期檢測線程T所需的某一資源R是否就緒。
如果線程T檢測到資源R尚未就緒,
則線程T阻塞一段時(shí)間后繼續(xù)循環(huán)檢測。sleep()方法//演示調(diào)用sleep()方法阻塞線程的用法public
classSleepThreadimplementsRunnable{
public
voidrun(){
while(true){ //執(zhí)行線程本身要完成的任務(wù)
... //給其他線程一個(gè)執(zhí)行的機(jī)會(huì)
try{ Thread.sleep(100); }catch(InterruptedExceptionexc){ //本線程的睡眠被另一線程中斷,例如:
//另一線程持有本線程的對(duì)象引用并調(diào)用interrupt()方法
... } } }}join()方法
讓線程進(jìn)入阻塞狀態(tài),等待另一正在運(yùn)行線程結(jié)束后恢復(fù)執(zhí)行。
狀態(tài)轉(zhuǎn)換:
RunningBlocked:調(diào)用另一個(gè)線程的join()方法。
BlockedRunning:取決于join()方法的參數(shù)。
帶時(shí)間參數(shù)版表示有兩個(gè)事件均會(huì)導(dǎo)致線程重返可執(zhí)行狀態(tài):
①另一線程執(zhí)行完畢。
②經(jīng)過的時(shí)間已超過指定的時(shí)間長度(毫秒)。
無時(shí)間參數(shù)版表示僅當(dāng)事件①出現(xiàn)時(shí)線程才重返可執(zhí)行狀態(tài)。
應(yīng)用實(shí)例:
通常結(jié)合isAlive()方法使用。
該方法返回線程是否已啟動(dòng)且未結(jié)束。
設(shè)線程t調(diào)用線程s的join()方法后阻塞,
則當(dāng)s.alive()為false時(shí)線程t重返可執(zhí)行狀態(tài)。
必須放在一個(gè)try-catch塊中處理。
因?yàn)閖oin()造成的阻塞可由interrupt()方法中斷。join()方法classSleeperextendsThread{
publicSleeper(Stringname){
super(name);start(); }
public
voidrun(){
try{ sleep(5000); }catch(InterruptedExceptione){ System.out.println(getName()+"睡眠被中斷。");
return; } System.out.println(getName()+"睡醒了!"); }}publicclassWaiterextendsThread{
privateSleepersleeper;
publicWaiter(Stringname,Sleepersleeper){
super(name);this.sleeper=sleeper;start(); }
public
voidrun(){
try{ sleeper.join(); }catch(InterruptedExceptionexc){} System.out.println(getName()+"完成聯(lián)合。"); }
public
static
voidmain(String[]args){ Sleepers1=newSleeper("S1"); Sleepers2=newSleeper("S2"); Waiterj1=newWaiter("J1",s1); Waiterj2=newWaiter("J2",s2); errupt(); }}S2睡眠被中斷。J2完成聯(lián)合。S1睡醒了!J1完成聯(lián)合。線程間通信的常用技巧是讓一個(gè)線程持有另一線程對(duì)象的引用wait()/notify()/notifyAll()方法
兩個(gè)方法配合使用,使線程進(jìn)入阻塞狀態(tài)后又恢復(fù)為可執(zhí)行狀態(tài)。
狀態(tài)轉(zhuǎn)換:
RunningBlocked:調(diào)用線程占有的鎖的wait()方法。
BlockedRunning:取決于wait()方法的參數(shù)。
帶時(shí)間參數(shù)版表示
溫馨提示
- 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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年公務(wù)員模擬測試題
- 春游踏青活動(dòng)策劃方案(3篇)
- 滅火應(yīng)急疏散設(shè)施管理制度(3篇)
- 疫情期間用水管理制度(3篇)
- 視頻監(jiān)控設(shè)備使用管理制度(3篇)
- 酒店室內(nèi)餐廳管理制度范本(3篇)
- 門店氣球策劃活動(dòng)方案(3篇)
- 項(xiàng)目組人員安全管理制度(3篇)
- 《GA 475-2004抗人血清試劑》專題研究報(bào)告
- 兼職安全員培訓(xùn)
- 北京市順義區(qū)近三年(2021-2023)七年級(jí)上學(xué)期期末試卷分類匯編:單項(xiàng)填空
- 化妝培訓(xùn)行業(yè)分析
- 集裝箱采購?fù)稑?biāo)方案(技術(shù)方案)
- 塔吊運(yùn)行日志
- 里氏硬度計(jì)算表
- 輸電線路基礎(chǔ)知識(shí)輸電線路組成與型式
- GB/T 14536.1-2022電自動(dòng)控制器第1部分:通用要求
- GB/T 24128-2009塑料防霉性能試驗(yàn)方法
- 土地買賣合同協(xié)議書模板
- 人員素質(zhì)測評(píng)理論與方法-素質(zhì)測評(píng)概述課件
- 微型消防站花名冊(cè)
評(píng)論
0/150
提交評(píng)論