安全程序設計:03 線程進程安全_第1頁
安全程序設計:03 線程進程安全_第2頁
安全程序設計:03 線程進程安全_第3頁
安全程序設計:03 線程進程安全_第4頁
安全程序設計:03 線程進程安全_第5頁
已閱讀5頁,還剩62頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

1、第三章線程/進程安全進程和線程是兩個范圍不同的概念。進程是程序在計算機上的一次執(zhí)行活動。運行一個程序,相當于啟動了一個進程進程是操作系統(tǒng)進行資源分配的單位,通俗地講,是一個正在執(zhí)行的程序線程是進程中的一個實體,是被系統(tǒng)獨立調(diào)度和分派的基本單位,它可與同屬一個進程的其它線程共享進程所擁有的全部資源一個線程可以創(chuàng)建和撤消另一個線程,同一進程中的多個線程之間可以并發(fā)執(zhí)行比如,一個在線播放軟件,在播放歌曲的同時還可以進行下載,就可認為這兩件工作由不同的線程完成。線程和進程的開發(fā)和相關操作,在程序設計中具有重要地位,線程和進程的安全和系統(tǒng)的安全息息相關。對于不夠熟練的程序員來說,很容易出現(xiàn)安全隱患,而這

2、些安全問題又具有不間斷發(fā)生,難于調(diào)試等特點一般說來,線程的安全性主要來源于其運行的并發(fā)性和對資源的共享性;進程的安全性主要在應用級別,在于其對系統(tǒng)的威脅性,不過對于系統(tǒng)軟件的開發(fā)者,進程安全的考慮需要更加深入本章主要針對線程和進程開發(fā)過程中的安全問題。首先基于面向對象語言,介紹線程的的基本機制,然后介紹線程操作過程中的幾個重要的安全問題:線程同步安全、線程協(xié)作安全、線程死鎖、線程控制,最后介紹進程安全3.1 線程機制3.1.1 為什么需要線程由于Java在線程操作方面具有較好的面向對象特性,也具有一定的代表性,因此本章基于Java語言進行介紹實際上,多線程最直觀的說法是:讓應用程序看起來好像同

3、時能做好幾件事情。為了表達這個問題,我們用一個案例來說明。比如,需要在控制臺上每隔1秒鐘打印一個歡迎信息。代碼如下所示: public class P03_01 public static void main(String args) while(true) System.out.println(Welcome); try Thread.sleep(1000); catch(Exception ex) System.out.println(其他工作); /代碼行1 該程序似乎沒有什么問題,運行時,Welcome也能不斷打印但是,我們發(fā)現(xiàn),打印函數(shù)中的while循環(huán)是個死循環(huán),也就是說,這個死循

4、環(huán)不運行完畢,程序將不能作其他事情比如,程序中的代碼行1永遠也無法運行。這就給程序的功能形成了巨大的阻礙 在實際應用開發(fā)的過程中,經(jīng)常會出現(xiàn)一個程序看起來同時作好幾件事情的情況,如程序進行一個用時較長的計算,希望該計算進行的時候,程序還可以做其他事情; 軟件要能夠接受多個客戶的請求,而讓客戶感覺不出等待; 媒體播放器在播放歌曲的同時也能下載; 財務軟件在后臺進行財務匯總的同時還能接受終端的請求;等等在這些情況下,多線程就能夠起到巨大的作用。線程和進程的關系很緊密,進程和線程是兩個不同的概念,但是進程的范圍大于線程。通俗地說,進程就是一個程序,線程是這個程序能夠同時做的各件事情。比如,媒體播放器

5、運行時就是一個進程,而媒體播放機同時做的下載文件和播放歌曲,就是兩個線程。以上代碼如果用線程來進行開發(fā),在Java語言里面,就可以用如P03_02.java的方式(其他語言類似) 。P03_02.java class WelcomeThread extends Thread public void run() while(true) System.out.println(Welcome); try Thread.sleep(1000); catch(Exception ex) public class P03_02 public static void main(String args) We

6、lcomeThread wt = new WelcomeThread( );wt.start( );/開啟線程System.out.println(其他工作);/代碼行1 運行,就會發(fā)現(xiàn),此時“打印歡迎信息”和“其他工作”就“同時”做了。3.1.2 線程機制和生命周期每個程序至少自動擁有一個線程,稱為主線程。當程序加載到內(nèi)存時,啟動主線程。從上節(jié)的程序可以看出,代碼行:實際上相當于實例化一個新的線程對象,并運行該線程中的run()函數(shù)。該線程的運行并不影響主線程向下執(zhí)行,這是為什么呢?WelcomeThread wt = new WelcomeThread(); wt.start(); 這是由

7、于多線程的機制實際上相當于CPU交替分配給不同的代碼段來運行:也就是說,某一個時間片,某線程運行,下一個時間片,另一個線程運行,各個線程都有搶占CPU的權利,至于決定哪個線程搶占,是操作系統(tǒng)需要考慮的事情。由于時間片的輪轉非???,用戶感覺不出各個線程搶占CPU的過程,看起來好像計算機在“同時”做好幾件事情一個線程有從創(chuàng)建、運行到消亡的過程,稱為線程的生命周期。用線程的狀態(tài)(state)表明線程處在生命周期的哪個階段線程有創(chuàng)建、可運行、運行中、阻塞、死亡五種狀態(tài)。通過線程的控制與調(diào)度可使線程在這幾種狀態(tài)間轉化線程的五種狀態(tài)創(chuàng)建狀態(tài):使用new運算符創(chuàng)建一個線程后,該線程僅僅是一個空對象,系統(tǒng)沒有

8、分配資源可運行狀態(tài):使用start()方法啟動一個線程后,系統(tǒng)分配了資源,使該線程處于可運行狀態(tài)(Runnable)運行中狀態(tài):占有CPU,執(zhí)行線程的run()方法阻塞狀態(tài):運行的線程因某種原因停止繼續(xù)運行死亡狀態(tài):線程結束 線程的安全隱患可能出現(xiàn)在各個狀態(tài)。一般說來,線程的安全性來源于兩個方面:1:多個線程之間可能會共享進程的內(nèi)存資源2:CPU的某個時間片分配給哪個線程使用,默認情況下無法由用戶控制多線程的安全問題比較復雜,解決方法繁多,在這里我們闡述幾個比較典型的安全問題 3.2 線程同步安全3.2.1 線程同步默認情況下,線程都是獨立的,而且異步執(zhí)行,線程中包含了運行時所需要的數(shù)據(jù)或方法

9、,而不需要外部的資源或方法,也不必關心其它線程的狀態(tài)或行為但是在多個線程在運行時共享數(shù)據(jù)的情況下,就需考慮其他線程的狀態(tài)和行為,否則就不能保證程序的運行結果的正確性在某些項目中,經(jīng)常會出現(xiàn)線程同步的問題,即:多個線程在訪問同一資源時,會出現(xiàn)安全問題所謂同步,就是在發(fā)出一個功能調(diào)用時,在沒有得到結果之前,該調(diào)用就不返回,同時其它線程也不能調(diào)用這個方法通俗地講,一個線程是否能夠搶占CPU,必須考慮另一個線程中的某種條件,而不能隨便讓操作系統(tǒng)按照默認方式分配CPU,如果條件不具備,就應該等待另一個線程運行,直到條件具備3.2.2 案例分析給出一個案例:有若干張飛機票,2個線程去賣它們,要求沒有票時能

10、夠提示:沒有票了以最后剩下3張票為例。首先用傳統(tǒng)方法來編寫這段代碼。代碼如P03_03.java所示。運行,控制臺打印如下: P03_03.java class TicketRunnable implements Runnable private int ticketNum = 3; public void run( ) while(true) String tName = Thread.currentThread().getName(); if ticketNum=0) System.out.println(tName + 無票); break; else ticketNum-;/代碼行1

11、System.out.println(tName + 賣出一張票,還剩+ticketNum +張票”); public class P03_03 public static void main(String args) TicketRunnable tr=new TicketRunnable(); Thread th1=new Thread(tr,線程1); Thread th1=new Thread(tr,線程2);th1.start(); th2.start(); 這段程序貌似沒有問題。但是它是很不安全的,并且這種不安全性很難發(fā)現(xiàn),會給項目后期維護帶來巨大的代價。觀察程序中的代碼行1處的注

12、釋,當只剩下一張票時,線程1賣出了最后一張票,接著要運行ticketNum-,但在ticketNum-還沒來得及運行的時候,線程2有可能搶占CPU,來判斷當前有無票可賣,此時,由于線程1還沒有將ticketNum-,當然票數(shù)還是1,線程2判斷還可以買票,這樣,最后一張票賣出了兩次。當然,上面的程序中,沒有給線程2以買票的機會,實際上票都由線程1賣出,我們看不出其中的問題。為了讓大家看清這個問題,我們模擬線程1和線程2交替賣票的情況。將P03_03.java的代碼改為P03_04.java:P03_04.java class TicketRunnable implements Runnable

13、private int ticketNum = 3; public void run( ) while(true) String tName = Thread.currentThread().getName(); if ticketNum=0) System.out.println(tName + 無票); break; else try Thread.sleep(1000); /休眠1000毫秒 catch(Exception ex) ticketNum-;/代碼行1 System.out.println(tName + 賣出一張票,還剩+ticketNum +張票”); public cl

14、ass P03_04 public static void main(String args) TicketRunnable tr=new TicketRunnable(); Thread th1=new Thread(tr,線程1); Thread th2=new Thread(tr,線程2); th1.start(); th2.start(); 該代碼中,增加了一行:程序休眠1000毫秒,讓另一個線程來搶占CPU。運行,控制臺打印如下: 最后一張票被賣出兩次,系統(tǒng)不可靠。更為嚴重的是,該問題的出現(xiàn)很具有隨機性。比如,有些項目在實驗室運行階段沒有問題,因為哪個線程搶占CPU,是由操作系統(tǒng)決定

15、的,用戶并沒有權利干涉,也無法預測,所以,項目可能在商業(yè)運行階段出現(xiàn)了問題,等到維護人員去查問題的時候,由于問題出現(xiàn)的隨機性,問題可能就不出現(xiàn)了。這種工作往往給維護帶來了巨大的代價。以上案例是多個線程消費有限資源的情況,該情況下還有很多其他案例,如:多個線程,向有限空間寫數(shù)據(jù)時:線程1寫完數(shù)據(jù),空間滿了,但沒來得及告訴系統(tǒng);此時另一個線程搶占CPU,也來寫,不知道空間已滿,造成溢出3.2.3 解決方案怎樣解決這個問題?很簡單,就是讓一個線程賣票時其他線程不能搶占CPU根據(jù)定義,實際上相當于要實現(xiàn)線程的同步,通俗地講,可以給共享資源(在本例中為票)加一把鎖,這把鎖只有一把鑰匙。哪個線程獲取了這把

16、鑰匙,才有權利訪問該共享資源有一種比較直觀的方法,可以在共享資源(如“票”)每一個對象內(nèi)部都增加一個新成員,標識“票”是否正在被賣中,其他線程訪問時,必須檢查這個標識,如果這個標識確定票正在被賣中,線程不能搶占CPU這種設計理論上當然也是可行,但由于線程同步的情況并不是很普遍,僅僅為了這種小概率事件,在所有對象內(nèi)部都開辟另一個成員空間,帶來極大的空間浪費,增加了編程難度,所以,一般不采用這種方法現(xiàn)代編程語言的設計思路都是把同步標識加在代碼段上,確切的說,是把同步標識放在“ 訪問共享資源(如賣票)的代碼段”上不同語言中,同步代碼段的實現(xiàn)模型類似,只是表達方式有些不同。這里以Java語言為例,在J

17、ava語言中,synchronized關鍵字可以解決這個問題,整個語法形式表現(xiàn)為: 注意,synchronized后的“同步鎖對象”,必須是可以被各個線程共享的,如this、某個全局變量等。不能是一個局部變量。其原理為:當某一線程運行同步代碼段時,在“同步鎖對象”上置一標記,運行完這段代碼,標記消除。其他線程要想搶占CPU運行這段代碼,必須在“同步鎖對象”上先檢查該標記,只有標記處于消除狀態(tài),才能搶占CPU。在上面的例子中,this是一個“同步鎖對象” synchronized(同步鎖對象) / 訪問共享資源,需要同步的代碼段 因此,在上面的案例中,可以將將賣票的代碼用synchronized

18、代碼塊包圍起來,“同步鎖對象”取this。如代碼P03_05.java所示。運行,可以得到如下效果。 P03_05.java class TicketRunnable implements Runnable private int ticketNum = 3; public void run( ) while(true) String tName = Thread.currentThread().getName();synchronized(this) if ticketNum=0) System.out.println(tName + 無票); break; else try Thread.

19、sleep(1000); /休眠1000毫秒 catch(Exception ex) ticketNum-;/代碼行1 System.out.println(tName + 賣出一張票,還剩+ticketNum +張票”); public class P03_05.這說明程序運行完全正常。從以上代碼可以看出,該方法的本質是將需要獨占CPU的代碼用synchronized(this)包圍起來。如前所述,一個線程進入這段代碼之后,就在this上加了一個標記,直到該線程將這段代碼運行完畢,才釋放這個標記。如果其他線程想要搶占CPU,先要檢查this上是否有這個標記。若有,就必須等待但是可以看出,該代

20、碼實際上運行較慢,因為一個線程的運行,必須等待另一個線程將同步代碼段運行完畢。因此,從性能上講,線程同步是非常耗費資源的一種操作。我們要盡量控制線程同步的代碼段范圍,理論上說,同步的代碼段范圍越小,段數(shù)越少越好實際上,在Java內(nèi),還可以直接把synchronized關鍵字直接加在函數(shù)的定義上,這也是一種可以推薦的方法不過,值得一提的是,如果不能確定整個函數(shù)都需要同步,那就要盡量避免直接把synchronized加在函數(shù)定義上的做法。如前所述,要控制同步粒度,同步的代碼段越小越好,synchronized控制的范圍越小越好,否則造成不必要的系統(tǒng)開銷。所以,在實際開發(fā)的過程中,要十分小心,因為過

21、多的線程等待可能造成系統(tǒng)性能的下降,甚至造成死鎖3.3 線程協(xié)作安全3.3.1 線程協(xié)作有些情況下,多個線程合作完成一件事情的幾個步驟,此時線程之間實現(xiàn)了協(xié)作如一個工作需要若干個步驟,各個步驟都比較耗時,不能因為它們的運行,影響程序的運行效果,最好的方法就是將各步用線程實現(xiàn)但是,由于線程隨時都有可能搶占CPU,可能在前面一個步驟沒有完成時,后面的步驟線程就已經(jīng)運行,該安全隱患造成系統(tǒng)得不到正確結果3.3.2 案例分析給出一個案例:線程1負責完成一個復雜運算(比較耗時),線程2負責得到結果,并將結果進行下一步處理。如:某個科學計算系統(tǒng)中,線程1負責計算1-1000各個數(shù)字的和(暫且認為它非常耗時

22、),線程2負責得到這個結果并且寫入數(shù)據(jù)庫讀者首先想到的是將耗時的計算放入線程。這是正確的想法。首先用傳統(tǒng)線程方法來編寫這段代碼,代碼如P03_06.java所示。P03_06.java public class P03_06 private int sum=0; public static void main(String args) new P03_06().cal(); public void cal() /完成工作步驟 Thread1 th1=new Thread1(); Thread2 th2=new Thread2(); th1.start(); th2.start(); class

23、 Thread1 extends Thread public void run() for (int i=1;i=1000;i+) sum+=i; class Thread2 extends Thread public void run( ) System.out.println( 寫入數(shù)據(jù)庫: + sum); 該程序貌似沒有問題,也能夠打印正確結果,但是和上一節(jié)的例子一樣,它也是很不安全的,這種不安全性也很難發(fā)現(xiàn),也會給項目后期維護帶來巨大的代價。該程序的安全隱患在哪里呢?觀察cal()函數(shù)中的代碼,當線程th1運行后,線程th2運行,此時,線程th2隨時可能搶占CPU,而不一定要等線程th

24、1運行完畢。當然,在上面的例子中,可能因為線程th1運行較快,th2在它運行的過程中沒有搶占CPU,“碰巧”得到了正確結果,但是如果讓線程th2搶占CPU,這樣,系統(tǒng)可能得不到正確結果。為了解釋這個問題,將P03_06.java的代碼改為P03_07.javaP03_07.java public class P03_07 private int sum=0; public static void main(String args) new P03_07().cal(); public void cal() /完成工作步驟 Thread1 th1=new Thread1(); Thread2 t

25、h2=new Thread2(); th1.start(); th2.start(); class Thread1 extends Thread public void run() for (int i=1;i=1000;i+) sum+=i; try Thread.sleep(1); /暫停1ms catch(Exception ex) class Thread2 extends Thread public void run( ) System.out.println( 寫入數(shù)據(jù)庫: + sum); 該代碼中,增加了一行:程序休眠1毫秒,讓另一個線程來搶占CPU。運行,控制臺打印如下: 很顯

26、然,這個結果不是我們所需要的。那為什么sum得到的結果為1呢?很明顯,線程th1的start函數(shù)運行時,相當于讓求和過程以多線程形式運行,在它“休眠”之際,th2就搶占了CPU,在求和還沒開始做或只完成一部分時就打印sum,導致得到不正常結果3.3.3 解決方案怎樣解決?顯而易見,方法是:在運行一個線程時,命令其他線程等待該線程運行完畢,才能搶占CPU進行運行對于該問題,不同語言解決方法類似。以Java語言為例,在Java語言中,線程的join()方法可以解決這個問題。見代碼P03_08.javaP03_08.java public class P03_08 private int sum=0

27、; public static void main(String args) new P03_08().cal(); public void cal() /完成工作步驟 Thread1 th1=new Thread1(); Thread2 th2=new Thread2(); th1.start(); try th1.join();/讓該線程運行完畢才能向下運行 catch(Exception ex) th2.start(); class Thread1 extends Thread public void run() for (int i=1;i=1000;i+) sum+=i; try T

28、hread.sleep(1); /暫停1ms catch(Exception ex) class Thread2 extends Thread public void run( ) System.out.println( 寫入數(shù)據(jù)庫: + sum); 運行正常。實際上,該程序相當于摒棄了“線程就是為了程序看起來同時做好幾件事情”的思想,將并發(fā)程序又變成了順序的,如果線程th1沒有運行完畢的話,程序會在th.join()處堵塞。如果cal()函數(shù)耗時較長,程序將一直等待一般的方法是,可以將該工作放在另一個線程中,這樣,既不會堵塞主程序,又能夠保證數(shù)據(jù)安全性見代碼P03_09.java publi

29、c class P03_09 implements Runnable private int sum=0; public static void main(String args) new Thread(new P03_09().start(); public void run() this.cal();/將cal()的調(diào)用放入線程 public void cal() /完成工作步驟 Thread1 th1=new Thread1(); Thread2 th2=new Thread2(); th1.start(); try th1.join();/讓該線程運行完畢才能向下運行 catch(Ex

30、ception ex) th2.start(); class Thread1 extends Thread public void run() for (int i=1;i=1000;i+) sum+=i; try Thread.sleep(1); /暫停1ms catch(Exception ex) class Thread2 extends Thread public void run( ) System.out.println( 寫入數(shù)據(jù)庫: + sum); 3.4 線程死鎖安全3.4.1 線程死鎖死鎖(DeadLock),是指兩個或兩個以上的線程在執(zhí)行過程中,因爭奪資源而造成的一種互相

31、等待的現(xiàn)象。此時稱系統(tǒng)處于死鎖狀態(tài),這些永遠在互相等待的線程稱為死鎖線程。產(chǎn)生死鎖的四個必要條件是:互斥條件:資源每次只能被一個線程使用。如前面的“線程同步代碼段”,就是只能被一個線程使用的典型資源;請求與保持條件:一個線程請求資源,但因為某種原因,該資源無法分配給它,于是該線程阻塞,此時,它對已獲得的資源保持不放;不剝奪條件:進程已獲得的資源,在未使用完之前,不管其是否阻塞,無法強行剝奪;循環(huán)等待條件:若干進程之間互相等待,形成一種頭尾相接的循環(huán)等待資源關系。這四個條件是死鎖的必要條件,只要系統(tǒng)發(fā)生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發(fā)生死鎖。3.4.2 案例分析以Jav

32、a語言為例,死鎖一般來源于代碼段的同步,當一段同步代碼被某線程運行時,其他線程可能進入堵塞狀態(tài)(無法搶占CPU),而剛好在該線程中,訪問了某個對象,此時,除非同步鎖定被解除,否則其他線程就不能訪問那個對象。這可以稱為“線程正在等待一個對象資源”。如果出現(xiàn)一種極端情況,一個線程等候另一個對象,而另一個對象又在等候下一個對象,以此類推。這個“ 等候鏈”如果進入封閉狀態(tài),也就是說,最后那個對象等候的是第一個對象,此時,所有線程都會陷入無休止的相互等待狀態(tài),造成死鎖。盡管這種情況并非經(jīng)常出現(xiàn),但一旦碰到,程序的調(diào)試將變得異常艱難。在這里給出一個死鎖的案例,如代碼P03_10.java public c

33、lass P03_10 implements Runnable static Object S1= new Object(),S2= new Object(); public void run() if(Thread.currentThread().getName().equals(th1) synchronized(S1) System.out.println(線程1鎖定S1);/代碼段1 synchronized(S2) System.out.println(線程1鎖定S2);/代碼段2 else synchronized(S2) System.out.println(線程2鎖定S2);/

34、代碼段3 synchronized(S1) System.out.println(線程2鎖定S1);/代碼段4 public static void main(String args) Thread t1=new Thread(new P03_10().th1); Thread t2=new Thread(new P03_10().th2); t1.start(); t2.start(); 這段程序也貌似沒有問題。但是和上一節(jié)的例子一樣,它也是很不安全的,這種不安全性也很難發(fā)現(xiàn)。觀察run()函數(shù)中的代碼,當th1運行后,進入代碼段1,鎖定了S1,如果此時th2運行,搶占CPU,進入代碼段3,

35、鎖定S2,那么th1就無法運行代碼段2,但是又沒有釋放S1,此時,th2也就不能運行代碼段4。造成互相等待。為了模擬這個過程,我們在程序中增加讓其休眠的代碼,好讓另一個線程來搶占CPU。將P03_10.java的代碼改為P03_11.java該代碼中,增加了一行:程序休眠1000毫秒,讓另一個線程來搶占CPU。運行,控制臺打印如下: 兩個線程陷入無休止的等待。其原因是,線程th1進入代碼段1后,線程2搶占CPU,鎖定了S2,而線程th1對S1的鎖定又沒有解除,造成線程th2無法運行下去,當然,由于線程th2鎖定了S2,線程th1也無法運行下去。死鎖是一個很重要的問題,它能導致整個應用程序慢慢終

36、止,尤其是當開發(fā)人員不熟悉如何分析死鎖環(huán)境的時候,還很難被分離和修復 public class P03_10 implements Runnable static Object S1= new Object(),S2= new Object(); public void run() if(Thread.currentThread().getName().equals(th1) synchronized(S1) System.out.println(線程1鎖定S1);/代碼段1 try Thread.sleep(1000); /暫停1000ms catch(Exception ex) synch

37、ronized(S2) System.out.println(線程1鎖定S2);/代碼段2 else synchronized(S2) System.out.println(線程2鎖定S2);/代碼段3 synchronized(S1) System.out.println(線程2鎖定S1);/代碼段4 public static void main(String args) Thread t1=new Thread(new P03_10().th1); Thread t2=new Thread(new P03_10().th2); t1.start(); t2.start(); 3.4.3

38、解決方案就語言本身來說,尚未直接提供防止死鎖的幫助措施,需要我們通過謹慎的設計來避免一般情況下,我們主要是針對死鎖產(chǎn)生的四個必要條件來進行破壞,用以避免和預防死鎖。在系統(tǒng)設計、線程開發(fā)等方面,注意如何不讓這四個必要條件成立,如何確定資源的合理分配算法,避免線程永久占據(jù)系統(tǒng)資源以Java為例,Java并不提供對死鎖的檢測機制。但可以通過java thread dump來進行判斷:一般情況下,當死鎖發(fā)生時,Java虛擬機處于掛起狀態(tài),thread dump可以給出靜態(tài)穩(wěn)定的信息,從操作系統(tǒng)上觀察,虛擬機的CPU占用率為零,這時可以收集thread dump,查找waiting for monito

39、r entry的線程,如果大量thread都在等待給同一個地址上鎖,說明很可能死鎖發(fā)生了解決死鎖沒有簡單的方法,這是因為線程產(chǎn)生死鎖都各有各的原因,而且往往具有很高的負載。從技術上講,可以用如下方法來進行死鎖排除:可以撤消陷于死鎖的全部線程可以逐個撤消陷于死鎖的進程,直到死鎖不存在從陷于死鎖的線程中逐個強迫放棄所占用的資源,直至死鎖消失提示:關于死鎖的檢測與解除,有很多重要算法,如資源分配算法、銀行家算法等。在操作系統(tǒng)的一些參考資料中應該可以進行足夠了解。很多軟件產(chǎn)品內(nèi)置了死鎖解決策略,可做參考。如: 數(shù)據(jù)庫死鎖。一個連接占用了另一個連接所需的數(shù)據(jù)庫鎖,它可能阻塞另一個連接。如果兩個或兩個以上

40、的連接相互阻塞,產(chǎn)生死鎖。該情況下,一般會強制銷毀一個連接(通常是使用最少的連接),并回滾其事務。這將釋放所有與已經(jīng)結束的事務相關聯(lián)的鎖,至少允許其他連接中有一個可以獲取它們正在被阻塞的鎖。資源池耗盡死鎖。資源池太小,而每個線程需要的資源超過了池中的可用資源,產(chǎn)生死鎖。此時可以增加連接池的大小或者重構代碼,以便單個線程不需要同時使用很多資源。3.5 線程控制安全3.5.1 安全隱患線程控制主要是對線程生命周期的一些操作,如暫停、繼續(xù)、消亡等。本節(jié)以Java語言為例,介紹線程控制中的一些安全問題。Java中提供了對線程生命周期進行控制的函數(shù): stop():停止線程; suspend():暫停線

41、程的運行; resume():繼續(xù)線程的運行: destroy():讓線程銷毀;等等。線程生命周期中的安全問題主要體現(xiàn)在: 線程暫?;蛘呓K止時,可能對某些資源的鎖并沒有釋放,它所保持的任何資源都會保持鎖定狀態(tài); 線程暫停之后,我們無法預計它什么時候會繼續(xù)(一般和用戶操作有關),如果對某個資源的鎖長期被保持,其他線程在任何時候都無法再次訪問該資源,極有可能造成死鎖。針對這個問題,為減少出現(xiàn)死鎖的可能,Java 1.2中,將Thread的stop(),suspend(),resume()以及destroy()方法定義為“已過時”方法,不再推薦使用。3.5.2 案例分析如前所述,線程的暫停和繼續(xù),早期采用suspend()和resume()方法,但是容易發(fā)生死鎖

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論