崔 妍
哈爾濱鐵道職業(yè)技術學院,黑龍江 哈爾濱 150081
創(chuàng)建線程需要一定的步驟,首先要創(chuàng)建線程,然后為其指定工作,當工作結整后再斃掉該線程。通常在Java中線程的編程形式通常有兩種,第一種所創(chuàng)建的線程類是經(jīng)過繼承Thread類實現(xiàn)的,再用該線程重載run();另外一種是建立一個Runnable接口類,因為Java無法支持多繼承性,因此如果要求類通過線程方式運行,并且繼承其它類,就要采用唯一的方法——run()來實現(xiàn)Runnable接口,該方法就成為線程主函數(shù)。不同的程序運行方式不同所產(chǎn)生的結果也必然不同,主要由于兩個原因,一是由于循環(huán)中有個隨機暫停,另外一個重要的原因是由于無法保證線程的執(zhí)行時間。Java按照其時間表來運行這些進程,每個線程可以有一個優(yōu)先級與其相關聯(lián)。
利用調(diào)度程序來完成線程的運行,調(diào)度方法有協(xié)作和搶先兩形式,相比之下?lián)屜仁皆谫Y源調(diào)度方面較之協(xié)作式有更大優(yōu)勢,Java恰恰支持搶先式,所以分配優(yōu)先級能力在多線程程式中十分重要,線程調(diào)度的決策就是以其為參照進行,程式中的某些部分也會根據(jù)其重要程度而變化,從而保證它的優(yōu)先級與其價值是相對應的。優(yōu)先級的設這要注意幾點:一是確定優(yōu)先級必須采用1~10之間的整數(shù);二是父線程的優(yōu)先級要被子線程所繼承;三是線程的優(yōu)先級可以通過setpriority()的調(diào)用進行改變。Java會先擇最高優(yōu)先級的線程來執(zhí)行,因此編寫程式時,要注意有些線程優(yōu)先級較低,但也要給其執(zhí)行的機會,此時優(yōu)先級高的線程可以適當進入休眠狀態(tài)。
在多線程程式中,所有線程都是各自獨立的執(zhí)行體,不過線程代碼的模式卻是相同的,如果這些線程要協(xié)同工作,那么一定要注意其線程代碼是不是能夠重入控制,所以Java就提供了相應的同步機制,從而阻止多個線程的一個或者多個關鍵代碼在任意時間執(zhí)行該代碼。該機制是建立在鎖的概念和監(jiān)視器基礎上的,其中監(jiān)視器是關鍵代碼周圍的保護,鎖則是監(jiān)視器阻止線程進入監(jiān)視器的軟件。其基本理念是:如果一個線程要進入監(jiān)視器所監(jiān)視的關鍵代碼,該線程要獲取一個與之相關的鎖;如果別的線程在使用這個鎖,那么Java就會強制性的要求其在一個與鎖以及監(jiān)視器相關的區(qū)域進行等待;當鎖被釋放時,Java就移出等待狀態(tài)中的線程,同意其獲取瑣,并對監(jiān)視器的關鍵代碼進行相應處理。Java自帶monitorexit以及monitorenter指令與鎖和監(jiān)視器同步工作,不過這種級別相對較低,我們可以使用以下兩種方法執(zhí)行線程同步:
把synchronized的關鍵字加入到方法聲明中,來聲明synchronized()方法。在synchronized()方法中,類成員變量訪問由synchronized()方法控制,每個類實例都會對應一個瑣,如果synchronized()方法要執(zhí)行就要獲取相應的鎖,否則會阻塞所屬的線程。一旦該方法進入執(zhí)行狀態(tài)就會獨占一個鎖,并且直至其返回才會釋放。這種機制可以確保在同一時間對每個類實例,它的聲明為synchronized的成員函數(shù)最多只能有一個處于執(zhí)行狀態(tài),從而避免了類成員變量訪問產(chǎn)生沖突。JAVA中每個類都對應一把這樣的鎖,因此要控制其訪問類的靜態(tài)成員變量,就可以把類的靜態(tài)成員函數(shù)聲明為synchronized。
上述方法中,如果代碼量較多,則聲明的效率會大受影響,因此我們可以用synchronized塊來解決該問題。synchronized塊中要獲取SynObject對應的鎖才可以執(zhí)行,因為可以對任何代碼塊并且上鎖的對象可以任意指定,因此相對synchronized塊方法有較高的靈活性。
既然進行多線程編程就不得不考慮訪問共享資源的問題。雖然上述JAVA同步機制可以控制線程代碼重入,但很多時候多個線程在各自的執(zhí)行過程中,會訪問諸如存儲處理等同一處資源,因為線程不同,其所執(zhí)行的時機也不一致,所以為了防止不同線程在訪問共享資源時修改其內(nèi)容,JAVA就提供了相應的阻塞機制來解決該問題,支持阻塞的方法有以下幾種:
1)線程阻塞的方法
第一種是sleep():該方法同意指定毫秒為單位的時間段作為參數(shù),可以使線程在其指定時間段內(nèi)不用得到CPU時間還可以進入阻塞狀態(tài),當指定時間過去后,線程即可進入執(zhí)行狀態(tài);第二種是resume()以及suspend():要將這兩種方法進行配合使用,suspend()會使線程保持阻塞狀態(tài),并且無法自動恢復,只有當調(diào)用resume()時,才可以重新使線程進入執(zhí)行狀態(tài);第三種是yield():該方法使得線程放棄當前得到的CPU時間,但是無法阻塞線程,因此線程仍然處于執(zhí)行狀態(tài)中,并且可以隨時再取得CPU時間。當調(diào)用yield()時其效果等價于調(diào)度程序,認為該線程已經(jīng)執(zhí)行了足夠的時間,轉(zhuǎn)達向另一個線程;第四種方法是notify()以及wait():要將兩種方法配合使用。其中wait()有兩種形式使線程進入阻塞狀態(tài),一種是同意指定一段時間為參數(shù),如果與其以對應的notify()超出所指定的時間或者被調(diào)用,線程可以重新執(zhí)行;另外一種是不指定參數(shù),此時就要等到調(diào)用notify()才可以解除。這種配套使用的方法與上述resume()、suspend()的區(qū)別就在于該方法阻塞時不會釋放鎖,而resume()、suspend()剛好相反。
2)注意事項
notify()和wait()屬于Thread類,而resume()和suspend()則屬于Object類,即所有對象都有這一對方法,在阻塞時要釋放所占用的鎖。由于任何對象都有鎖,所以調(diào)用任何對象wait()法都會導致線程阻塞,且對象的鎖也被釋放;如果調(diào)用任何對象notify()法,就會導致調(diào)用該對象的wait()法阻塞線程中隨機選擇任意一個解除阻塞。此外,調(diào)用不指定超時期限的wait()法以及suspend()法,都有產(chǎn)生死鎖的可能,JAVA在語言級別上并不支持避免死鎖,因此在編程過程中要注意加以控制,避免死鎖。
[1]王昕.Java多線程機制在并發(fā)編程中的應用[J].現(xiàn)代商貿(mào)工業(yè),2010(17).
[2]張迎,徐洪珍,湯彬.耘夕扣多線程技術及其在網(wǎng)絡編程中的應用[J].科技廣場,2008(5).
[3]李丹塞.Java多線程編程技術試議[J].軟件開發(fā)與設計,2010(2).
[4]賴萬欽.JAVA多線程編程技術探討[J].福建電腦,2008(6).
[5]袁云,邵時.基于多核處理器并行系統(tǒng)的任務調(diào)度算法[J].計算機應用,2008(28).