孫明鵬 胡飛 胡長春
【摘要】多線程編程的基本概念存在于研究與開發(fā)實(shí)驗(yàn)室中已有數(shù)十年之久。多線程的真正價值在語言設(shè)計的理論基礎(chǔ)和實(shí)戰(zhàn)細(xì)節(jié)上是顯而易見的,利用現(xiàn)代處理器和多處理器及其特點(diǎn),多線程可具有更好的性能。多線程的應(yīng)用最重要的問題是線程安全,未解決和解決不完全的線程安全問題,其導(dǎo)致的錯誤很難發(fā)現(xiàn)且很難被調(diào)試,并且線程所導(dǎo)致的錯誤所造成的損失是巨大且無可估量的。
【關(guān)鍵詞】多線程 ?編程 ?安全
一、什么是線程安全
當(dāng)你的程序所在的進(jìn)程之中有多個線程在同時運(yùn)行,而這些線程可能會同時運(yùn)行一段代碼或者會同時訪問一個對象,如果每次運(yùn)行這段代碼或?qū)ο笤L問之后,所得到的結(jié)果和單線程情況下運(yùn)行結(jié)果一樣,而且其他的變量的值也和預(yù)期保持一致,我們就認(rèn)為是線程安全的。也就是說,當(dāng)多個線程同時運(yùn)行同一段代碼時不會造成資源的沖突,不會產(chǎn)生錯誤的結(jié)果就是線程安全的。如過有一段線程安全的代碼,它在多個線程中使用時不需要做同步處理;而線程不安全的代碼在多線程環(huán)境中使用必須要做同步處理,否則將會出現(xiàn)不可預(yù)期的后果。
二、線程不安全所造成的影響
在不進(jìn)行數(shù)據(jù)保護(hù)操作時,當(dāng)一段代碼或者對象可能被多個線程訪問,而且訪問順序不能確定,那么一旦這段代碼或者對象處于有條件的線程安全,線程兼容,線程對立這一分類,就可能出現(xiàn)多個線程先后更改數(shù)據(jù),從而造成得到的數(shù)據(jù)不一致,或者數(shù)據(jù)出現(xiàn)污染,更嚴(yán)重的時候會出現(xiàn)臟數(shù)據(jù),甚至程序崩潰。線程不安全是可怕的,一方面它對于程序編寫人員來說是一場災(zāi)難,因?yàn)榫€程不安全導(dǎo)致的錯誤會被程序復(fù)雜的邏輯掩蓋,它所造成的錯誤與邏輯不嚴(yán)謹(jǐn)所造成的錯誤基本上相似,這就為除錯造成了困難,很多時候需要重新審視整個代碼,甚至很長時間無法發(fā)現(xiàn)問題所在。另一方面它對于程序的使用者造成的損失基本無法估計,在當(dāng)下的數(shù)據(jù)時代,任何數(shù)據(jù)的不一致,數(shù)據(jù)的污染都會給一個公司和個人造成無法挽回的影響。
三、線程不安全的分類
線程的安全程度并沒有一個統(tǒng)一的分類,在喬希·布洛赫所給出線程分類描述,線程安全性分為五類,分別是:不可變、線程安全、有條件線程安全、線程兼容和線程對立。當(dāng)然,若在明確的明白下線程安全特性的情況下,無論是否使用這種分類系統(tǒng)都沒有關(guān)系。喬希·布洛赫所提出的分類方法也有局限性,各個分類之間的界限并不是絕對的明朗,但是作為對線程安全的分類,確是簡單明了的。下面就各個類別進(jìn)行詳細(xì)的說明:
1,不可變的
不可變的對象一定是線程安全的,因?yàn)槠洳豢勺兊奶匦?,它永遠(yuǎn)也不需要進(jìn)行額外的同步操作。只要一個不可變的對象在構(gòu)建時候是正確的,那么永遠(yuǎn)也不會看到它處于不一致的狀態(tài)。再代碼中,基本類型,基本數(shù)值類,字符常量都是不可變的。
2,線程安全的
所有線程安全的代碼或者對象都具有上面所提到的線程安全的屬性,也就是說,這段代碼或?qū)ο笤诙嗑€程環(huán)境下,被多個線程訪問,不管這些線程的訪問順序是何種順序,所有訪問的線程都不需要進(jìn)行額外的同步工作。這種線程安全的要求是嚴(yán)格的,滿足這種要求的和滿足不可變類型的所有代碼或者對象都是絕對的線程安全。
3,有條件的線程安全
有條件的線程安全,指的是對于這段代碼或者對象僅僅在一些操作訪問順序不同需要額外的同步操作。最顯著的有條件的線程安全是在遍歷一些返回迭代器對象的時候,如果在遍歷這些對象的同時存在另一個或多個線程進(jìn)行了添加或移除內(nèi)部元素的操作,必然會出現(xiàn)迭代器的失效。為了保證在某一線程在執(zhí)行遍歷操作的時候該對象不會被其他線程訪問,應(yīng)通過相應(yīng)的手段,阻止其他線程做出導(dǎo)致該對象的迭代器失效的操作。進(jìn)行迭代的線程應(yīng)該確保它是獨(dú)占性的訪問該對象,從而保證了遍歷的完整性。
4,線程兼容的
線程兼容的代碼或者對象不是線程安全的,但是可以通過正常使用同步而在多線程環(huán)境下安全的使用。
5,線程對立的
線程對立是指一段代碼或者對象在運(yùn)行中,一旦存在多進(jìn)程對它進(jìn)行操作,總是不能保證線程安全的,這種不能保證不管是否進(jìn)行了同步的操作。線程對立是一種罕見且特殊的情況,例如當(dāng)一段代碼或?qū)ο笾械撵o態(tài)數(shù)據(jù)被修改之后,被修改的數(shù)據(jù)可能會影響到其他代碼或者其他對象的行為時,這種時候就會出現(xiàn)線程對立。
四、防范線程不安全影響的措施
1.對于可能會運(yùn)行在多線程環(huán)境下的代碼和對象的編寫人員,應(yīng)盡量的保證該代碼在多線程環(huán)境每次運(yùn)行結(jié)果和單線程情況下運(yùn)行結(jié)果一致,而且其他的變量和值也能和預(yù)期結(jié)果保持一致。這種情況從根本上保證了線程安全。
2.對于可能會運(yùn)行在多線程環(huán)境下的代碼和對象的編寫人員,在編寫代碼和對象的時候,應(yīng)注意辨別是否能在多線程下正確運(yùn)行,根據(jù)上文提出的線程安全的分類,對于有條件的線程安全的,線程兼容的,線程對立的應(yīng)該給以相應(yīng)的標(biāo)記,或在文檔之中詳細(xì)的寫出說明。保證該代碼或者對象的潛在使用者能夠通過查看注釋或文檔,清楚明白的知道該代碼或?qū)ο笤诙嗑€程環(huán)境下的運(yùn)行狀態(tài)。
3.對于某段代碼或者某個對象的使用者,在使用這段代碼或?qū)ο笾?,?yīng)該清楚的知道該代碼和對象是否是線程安全的,并且應(yīng)該清楚的知道接下來使用這段代碼或?qū)ο蟮倪\(yùn)行環(huán)境是否為多線程環(huán)境,根據(jù)不同的情況決定是否需要進(jìn)行線程間的同步,或者用互斥鎖等方法使得線程能夠有序的訪問該代碼或?qū)ο蟆?/p>
4.對于某段代碼或者某個對象的使用者,如果確定為多線程環(huán)境,且確定接下來使用的代碼或?qū)ο笥锌赡軙欢鄠€線程訪問,并且無法確定其是否是線程安全的時候,應(yīng)盡量通過多種方式確定其線程安全與否,這些方式包括聯(lián)系作者或者通過自己的測試,以及查看源代碼。
5.對于軟件設(shè)計者來說,當(dāng)程序不能按照設(shè)計運(yùn)行,應(yīng)當(dāng)在檢測邏輯錯誤的同時,檢測是否因?yàn)榫€程不安全而導(dǎo)致的錯誤是必要的。依次檢查所使用別人代碼或別人提供的對象是否存在線程安全問題是最為合適的。
6.對于已經(jīng)發(fā)現(xiàn)因?yàn)榫€程安全問題導(dǎo)致錯誤的軟件使用者,應(yīng)及時停止使用該軟件,及時聯(lián)系軟件廠商,要求修改。
參考文獻(xiàn):
1 Lubomir F.Bic,Alan C.Shaw.操作系統(tǒng)原理[M].北京:清華大學(xué)出版社,2005
2 安德魯斯.多線程,并行與分布式程序設(shè)計基礎(chǔ)(影印版)[M].北京:高等教育出版社,2002
3 Bruce Eckel.Thinking in JAVA[M].London:Prentice Hall,2006