文章編號:11433時間:2024-09-30人氣:
在 Java 中,線程暫停是一個重要的概念,它允許線程在一定時間內(nèi)停止執(zhí)行。通過使用
suspend()
方法,可以將線程置于掛起狀態(tài),并阻止其繼續(xù)執(zhí)行。
suspend()
方法是
Thread
類中定義的一個方法。其簽名如下:```javapublic final void suspend()```當(dāng)調(diào)用
suspend()
方法時,當(dāng)前線程將進(jìn)入掛起狀態(tài)。在這種狀態(tài)下,線程不會執(zhí)行任何代碼,并且不會占用任何 CPU 時間。
suspend()
方法的使用非常簡單。要暫停線程,只需調(diào)用該方法即可:```javaThread thread = new Thread();thread.suspend();```
ART 深入淺出() 崩潰原因剖析() 在卡頓檢測中常被調(diào)用,但頻繁調(diào)用可能導(dǎo)致崩潰,崩潰堆棧通常顯示為:VMStack_getThreadStackTrace() -> ThreadList::SuspendThreadByPeer() 等。 本文將逐步解析其崩潰機(jī)制。 在 ART (Android Runtime) 的源碼 Android 12 版本中, 的 GetThreadStack 函數(shù)是關(guān)鍵,它涉及線程掛起和回調(diào)生成調(diào)用棧的過程。 首先,通過 SuspendThreadByPeer() 函數(shù)掛起線程,然后回調(diào)生成調(diào)用棧,最后恢復(fù)線程。 然而,這個過程可能因超時引發(fā)問題,例如當(dāng) SuspendThreadByPeer() 在線程狀態(tài)檢查中判斷線程未掛起時,超時會觸發(fā) ThreadSuspendByPeerWarning(),嚴(yán)重時會導(dǎo)致 Runtime::Abort。 通常,使用 ThreadList::SuspendThreadByThreadId() 函數(shù)可以避免這種 Crash,因?yàn)樗诔瑫r后只會產(chǎn)生警告,而不是終止。 超時時間默認(rèn)為 10 秒,如果線程長時間未能掛起,可能源于 ART 掛起線程的機(jī)制。 在舊版 ART 中,掛起線程通過 ModifySuspendCount() 函數(shù)設(shè)置標(biāo)志位,但在新版本中,這個邏輯已有所改變。 深入探究,Java 的 Check Point 概念在其中起關(guān)鍵作用。 解釋執(zhí)行的 switch/case 語句和機(jī)器碼執(zhí)行都有檢查點(diǎn),這些檢查點(diǎn)會暫停線程執(zhí)行,進(jìn)行垃圾回收、調(diào)試等操作。 當(dāng) () 觸發(fā)掛起時,會進(jìn)入 CheckSuspend() 函數(shù),依據(jù)狀態(tài)標(biāo)志位決定掛起或執(zhí)行檢查點(diǎn)。 真正的掛起操作會在析構(gòu)函數(shù)中執(zhí)行,通過 wait 函數(shù)掛起線程,直到其他線程執(zhí)行到喚醒操作。 總結(jié)來說,() 崩潰源于線程掛起操作與檢查點(diǎn)執(zhí)行的同步問題。 當(dāng)線程未能及時進(jìn)入檢查點(diǎn),getStackTrace() 的等待時間過長,從而導(dǎo)致崩潰。 理解了這個機(jī)制,就能避免此類問題的發(fā)生。
線程的同步是Java多線程編程的難點(diǎn),往往開發(fā)者搞不清楚什么是競爭資源、什么時候需要考慮同步,怎么同步等等問題,當(dāng)然,這些問題沒有很明確的答案,但有些原則問題需要考慮,是否有競爭資源被同時改動的問題?對于同步,在具體的Java代碼中需要完成以下兩個操作:把競爭訪問的資源標(biāo)識為private;同步哪些修改變量的代碼,使用synchronized關(guān)鍵字同步方法或代碼。
當(dāng)然這不是唯一控制并發(fā)安全的途徑。
synchronized關(guān)鍵字使用說明synchronized只能標(biāo)記非抽象的方法,不能標(biāo)識成員變量。
為了演示同步方法的使用,構(gòu)建了一個信用卡賬戶,起初信用額為100w,然后模擬透支、存款等多個操作。
顯然銀行賬戶User對象是個競爭資源,而多個并發(fā)操作的是賬戶方法oper(int x),當(dāng)然應(yīng)該在此方法上加上同步,并將賬戶的余額設(shè)為私有變量,禁止直接訪問。
工作原理線程是進(jìn)程中的實(shí)體,一個進(jìn)程可以擁有多個線程,一個線程必須有一個父進(jìn)程。
線程不擁有系統(tǒng)資源,只有運(yùn)行必須的一些數(shù)據(jù)結(jié)構(gòu);它與父進(jìn)程的其它線程共享該進(jìn)程所擁有的全部資源。
線程可以創(chuàng)建和撤消線程,從而實(shí)現(xiàn)程序的并發(fā)執(zhí)行。
一般,線程具有就緒、阻塞和運(yùn)行三種基本狀態(tài)。
在多中央處理器的系統(tǒng)里,不同線程可以同時在不同的中央處理器上運(yùn)行,甚至當(dāng)它們屬于同一個進(jìn)程時也是如此。
大多數(shù)支持多處理器的操作系統(tǒng)都提供編程接口來讓進(jìn)程可以控制自己的線程與各處理器之間的關(guān)聯(lián)度(affinity)。
有時候,線程也稱作輕量級進(jìn)程。
就象進(jìn)程一樣,線程在程序中是獨(dú)立的、并發(fā)的執(zhí)行路徑,每個線程有它自己的堆棧、自己的程序計(jì)數(shù)器和自己的局部變量。
但是,與分隔的進(jìn)程相比,進(jìn)程中的線程之間的隔離程度要小。
它們共享內(nèi)存、文件句柄和其它每個進(jìn)程應(yīng)有的狀態(tài)。
進(jìn)程可以支持多個線程,它們看似同時執(zhí)行,但互相之間并不同步。
一個進(jìn)程中的多個線程共享相同的內(nèi)存地址空間,這就意味著它們可以訪問相同的變量和對象,而且它們從同一堆中分配對象。
盡管這讓線程之間共享信息變得更容易,但您必須小心,確保它們不會妨礙同一進(jìn)程里的其它線程。
Java 線程工具和 API看似簡單。
但是,編寫有效使用線程的復(fù)雜程序并不十分容易。
因?yàn)橛卸鄠€線程共存在相同的內(nèi)存空間中并共享相同的變量,所以您必須小心,確保您的線程不會互相干擾。
線程屬性為了正確有效地使用線程,必須理解線程的各個方面并了解Java 實(shí)時系統(tǒng)。
必須知道如何提供線程體、線程的生命周期、實(shí)時系統(tǒng)如 何調(diào)度線程、線程組、什么是幽靈線程(Demo nThread)。
線程體所有的操作都發(fā)生在線程體中,在Java中線程體是從Thread類繼承的run()方法,或?qū)崿F(xiàn)Runnable接口的類中的run()方法。
當(dāng)線程產(chǎn)生并初始化后,實(shí)時系統(tǒng)調(diào)用它的run()方法。
run()方法內(nèi)的代碼實(shí)現(xiàn)所產(chǎn)生線程的行為,它是線程的主要部分。
線程狀態(tài)附圖表示了線程在它的生命周期內(nèi)的任何時刻所能處的狀態(tài)以及引起狀態(tài)改變的方法。
這圖并不是完整的有限狀態(tài)圖,但基本概括了線程中比較感興趣和普遍的方面。
以下討論有關(guān)線程生命周期以此為據(jù)。
●新線程態(tài)(New Thread)產(chǎn)生一個Thread對象就生成一個新線程。
當(dāng)線程處于新線程狀態(tài)時,僅僅是一個空線程對象,它還沒有分配到系統(tǒng)資源。
因此只能啟動或終止它。
任何其他操作都會引發(fā)異常。
例如,一個線程調(diào)用了new方法之后,并在調(diào)用start方法之前的處于新線程狀態(tài),可以調(diào)用start和stop方法。
●可運(yùn)行態(tài)(Runnable)start()方法產(chǎn)生運(yùn)行線程所必須的資源,調(diào)度線程執(zhí)行,并且調(diào)用線程的run()方法。
在這時線程處于可運(yùn)行態(tài)。
該狀態(tài)不稱為運(yùn)行態(tài)是因?yàn)檫@時的線程并不總是一直占用處理機(jī)。
特別是對于只有一個處理機(jī)的PC而言,任何時刻只能有一個處于可運(yùn)行態(tài)的線程占用處理 機(jī)。
Java通過調(diào)度來實(shí)現(xiàn)多線程對處理機(jī)的共享。
注意,如果線程處于Runnable狀態(tài),它也有可能不在運(yùn)行,這是因?yàn)檫€有優(yōu)先級和調(diào)度問題。
●阻塞/非運(yùn)行態(tài)(Not Runnable)當(dāng)以下事件發(fā)生時,線程進(jìn)入非運(yùn)行態(tài)。
①suspend()方法被調(diào)用;②sleep()方法被調(diào)用;③線程使用wait()來等待條件變量;④線程處于I/O請求的等待。
●死亡態(tài)(Dead)當(dāng)run()方法返回,或別的線程調(diào)用stop()方法,線程進(jìn)入死亡態(tài)。
通常Applet使用它的stop()方法來終止它產(chǎn)生的所有線程。
線程的本操作:派生:線程在進(jìn)程內(nèi)派生出來,它即可由進(jìn)程派生,也可由線程派生。
阻塞(Block):如果一個線程在執(zhí)行過程中需要等待某個事件發(fā)生,則被阻塞。
激活(unblock):如果阻塞線程的事件發(fā)生,則該線程被激活并進(jìn)入就緒隊(duì)列。
調(diào)度(schedule):選擇一個就緒線程進(jìn)入執(zhí)行狀態(tài)。
結(jié)束(Finish):如果一個線程執(zhí)行結(jié)束,它的寄存器上下文以及堆棧內(nèi)容等將被釋放。
圖2 線程的狀態(tài)與操作線程的另一個執(zhí)行特性是同步。
線程中所使用的同步控制機(jī)制與進(jìn)程中所使用的同步控制機(jī)制相同。
線程優(yōu)先級雖然我們說線程是并發(fā)運(yùn)行的。
然而事實(shí)常常并非如此。
正如前面談到的,當(dāng)系統(tǒng)中只有一個CPU時,以某種順序在單CPU情況下執(zhí)行多線程被稱為調(diào)度(scheduling)。
Java采用的是一種簡單、固定的調(diào)度法,即固定優(yōu)先級調(diào)度。
這種算法是根據(jù)處于可運(yùn)行態(tài)線程的相對優(yōu)先級來實(shí)行調(diào)度。
當(dāng)線程產(chǎn)生時,它繼承原線程的優(yōu)先級。
在需要時可對優(yōu)先級進(jìn)行修改。
在任何時刻,如果有多條線程等待運(yùn)行,系統(tǒng)選擇優(yōu)先級最高的可運(yùn)行線程運(yùn)行。
只有當(dāng)它停止、自動放棄、或由于某種原因成為非運(yùn)行態(tài)低優(yōu)先級的線程才能運(yùn)行。
如果兩個線程具有相同的優(yōu)先級,它們將被交替地運(yùn)行。
Java實(shí)時系統(tǒng)的線程調(diào)度算法還是強(qiáng)制性的,在任何時刻,如果一個比其他線程優(yōu)先級都高的線程的狀態(tài)變?yōu)榭蛇\(yùn)行態(tài),實(shí)時系統(tǒng)將選擇該線程來運(yùn)行。
一個應(yīng)用程序可以通過使用線程中的方法setPriority(int),來設(shè)置線程的優(yōu)先級大小。
有線程進(jìn)入了就緒狀態(tài),需要有線程調(diào)度程序來決定何時執(zhí)行,根據(jù)優(yōu)先級來調(diào)度。
線程中的join()可以用來邀請其他線程先執(zhí)行(示例代碼如下);publicclassJoin01implementsRunnable{publicstaticvoidmain(String[]args){for(inti=0;i<20;i++){if(i==5){Join01j=newJoin01();Threadt=newThread(j);(被邀請先執(zhí)行的線程.);();try{//邀請這個線程,先執(zhí)行();}catch(InterruptedExceptione){();}}(沒被邀請的線程。
+(i+1));}}publicvoidrun(){for(inti=0;i<10;i++){(()()+(i+1));}}}yield()告訴系統(tǒng)把自己的CPU時間讓掉,讓其他線程或者自己運(yùn)行,示例代碼如下;publicclassYield01{publicstaticvoidmain(String[]args){YieldFirstyf=newYieldFirst();YieldSecondys=newYieldSecond();YieldThirdyt=newYieldThird();();();();}}classYieldFirstextendsThread{@Overridepublicvoidrun(){for(inti=0;i<10;i++){(第一個線程第+(i+1)+次運(yùn)行.);//讓當(dāng)前線程暫停yield();}}}classYieldSecondextendsThread{@Overridepublicvoidrun(){for(inti=0;i<10;i++){(第二個線程第+(i+1)+次運(yùn)行.);//讓當(dāng)前線程暫停yield();在CAS中的compare就需要用它作為條件。
在拷貝完object mark word之后,JVM做了一步交換指針的操作,即流程中第一個橙色矩形框內(nèi)容所述。
將object mark word里的輕量級鎖指針指向lock record所在的stack指針,作用是讓其他線程知道,該object monitor已被占用。
lock record里的owner指針指向object mark word的作用是為了在接下里的運(yùn)行過程中,識別哪個對象被鎖住了。
最后一步unlock中,我們發(fā)現(xiàn),JVM同樣使用了CAS來驗(yàn)證object mark word在持有鎖到釋放鎖之間,有無被其他線程訪問。
如果其他線程在持有鎖這段時間里,嘗試獲取過鎖,則可能自身被掛起,而mark word的重量級鎖指針也會被相應(yīng)修改。
此時,unlock后就需要喚醒被掛起的線程。
偏向鎖Java偏向鎖(Biased Locking)是Java 6引入的一項(xiàng)多線程優(yōu)化。
它通過消除資源無競爭情況下的同步原語,進(jìn)一步提高了程序的運(yùn)行性能。
它與輕量級鎖的區(qū)別在于,輕量級鎖是通過CAS來避免進(jìn)入開銷較大的互斥操作,而偏向鎖是在無競爭場景下完全消除同步,連CAS也不執(zhí)行(CAS本身仍舊是一種操作系統(tǒng)同步原語,始終要在JVM與OS之間來回,有一定的開銷)。
所謂的無競爭場景,就是單線程訪問帶同步的資源或方法。
偏向鎖,顧名思義,它會偏向于第一個訪問鎖的線程,如果在接下來的運(yùn)行過程中,該鎖沒有被其他的線程訪問,則持有偏向鎖的線程將永遠(yuǎn)不需要觸發(fā)同步。
如果在運(yùn)行過程中,遇到了其他線程搶占鎖,則持有偏向鎖的線程會被掛起,JVM會嘗試消除它身上的偏向鎖,將鎖恢復(fù)到標(biāo)準(zhǔn)的輕量級鎖。
(偏向鎖只能在單線程下起作用)。
偏向模式和非偏向模式,在mark word表中,主要體現(xiàn)在thread ID字段是否為空。
掛起持有偏向鎖的線程,這步操作類似GC的pause,但不同之處是,它只掛起持有偏向鎖的線程(非當(dāng)前線程)。
在搶占模式的橙色區(qū)域說明中有提到,指向當(dāng)前堆棧中最近的一個lock record(在輕量級鎖中,lock record是進(jìn)入鎖前會在stack上創(chuàng)建的一份內(nèi)存空間)。
這里提到的最近的一個lock record,其實(shí)就是當(dāng)前鎖所在的stack frame上分配的lock record。
整個步驟是從偏向鎖恢復(fù)到輕量級鎖的過程。
偏向鎖也會帶來額外開銷。
在JDK6中,偏向鎖是默認(rèn)啟用的。
它提高了單線程訪問同步資源的性能。
但試想一下,如果你的同步資源或代碼一直都是多線程訪問的,那么消除偏向鎖這一步驟對你來說就是多余的。
事實(shí)上,消除偏向鎖的開銷還是蠻大的。
所以在你非常熟悉自己的代碼前提下,大可禁用偏向鎖 -XX:-UseBiasedLocking。
分類線程有兩個基本類型:用戶級線程:管理過程全部由用戶程序完成,操作系統(tǒng)內(nèi)核心只對進(jìn)程進(jìn)行管理。
系統(tǒng)級線程(核心級線程):由操作系統(tǒng)內(nèi)核進(jìn)行管理。
操作系統(tǒng)內(nèi)核給應(yīng)用程序提供相應(yīng)的系統(tǒng)調(diào)用和應(yīng)用程序接口API,以使用戶程序可以創(chuàng)建、執(zhí)行、撤消線程。
舉例UNIX International 線程UNIX International 線程的頭文件是
一、線程的概念一般來說,我們把正在計(jì)算機(jī)中執(zhí)行的程序叫做進(jìn)程(Process) ,而不將其稱為程序(Program)。 所謂線程(Thread),是進(jìn)程中某個單一順序的控制流。 新興的操作系統(tǒng),如Mac,Windows NT,Windows 95等,大多采用多線程的概念,把線程視為基本執(zhí)行單位。 線程也是Java中的相當(dāng)重要的組成部分之一。 甚至最簡單的Applet也是由多個線程來完成的。 在Java中,任何一個Applet的paint()和update()方法都是由AWT(Abstract Window Toolkit)繪圖與事件處理線程調(diào)用的,而Applet 主要的里程碑方法——init(),start(),stop()和destory()——是由執(zhí)行該Applet的應(yīng)用調(diào)用的。 單線程的概念沒有什么新的地方,真正有趣的是在一個程序中同時使用多個線程來完成不同的任務(wù)。 某些地方用輕量進(jìn)程(Lightweig ht Process)來代替線程,線程與真正進(jìn)程的相似性在于它們都是單一順序控制流。 然而線程被認(rèn)為輕量是由于它運(yùn)行于整個程序的上下文內(nèi),能使用整個程序共有的資源和程序環(huán)境。 作為單一順序控制流,在運(yùn)行的程序內(nèi)線程必須擁有一些資源作為必要的開銷。 例如,必須有執(zhí)行堆棧和程序計(jì)數(shù)器。 在線程內(nèi)執(zhí)行的代碼只在它的上下文中起作用,因此某些地方用執(zhí)行上下文來代替線程。 二、線程屬性為了正確有效地使用線程,必須理解線程的各個方面并了解Java 實(shí)時系統(tǒng)。 必須知道如何提供線程體、線程的生命周期、實(shí)時系統(tǒng)如 何調(diào)度線程、線程組、什么是幽靈線程(Demo nThread)。 (1)線程體所有的操作都發(fā)生在線程體中,在Java中線程體是從Thread類繼承的run()方法,或?qū)崿F(xiàn)Runnable接口的類中的run()方法。 當(dāng)線程產(chǎn)生并初始化后,實(shí)時系統(tǒng)調(diào)用它的run()方法。 run()方法內(nèi)的代碼實(shí)現(xiàn)所產(chǎn)生線程的行為,它是線程的主要部分。 (2)線程狀態(tài)附圖表示了線程在它的生命周期內(nèi)的任何時刻所能處的狀態(tài)以及引起狀態(tài)改變的方法。 這圖并不是完整的有限狀態(tài)圖,但基本概括了線程中比較感興趣和普遍的方面。 以下討論有關(guān)線程生命周期以此為據(jù)。 ●新線程態(tài)(New Thread)產(chǎn)生一個Thread對象就生成一個新線程。 當(dāng)線程處于新線程狀態(tài)時,僅僅是一個空線程對象,它還沒有分配到系統(tǒng)資源。 因此只能啟動或終止它。 任何其他操作都會引發(fā)異常。 ●可運(yùn)行態(tài)(Runnable)start()方法產(chǎn)生運(yùn)行線程所必須的資源,調(diào)度線程執(zhí)行,并且調(diào)用線程的run()方法。 在這時線程處于可運(yùn)行態(tài)。 該狀態(tài)不稱為運(yùn)行態(tài)是因?yàn)檫@時的線程并不總是一直占用處理機(jī)。 特別是對于只有一個處理機(jī)的PC而言,任何時刻只能有一個處于可運(yùn)行態(tài)的線程占用處理 機(jī)。 Java通過調(diào)度來實(shí)現(xiàn)多線程對處理機(jī)的共享。 ●非運(yùn)行態(tài)(Not Runnable)當(dāng)以下事件發(fā)生時,線程進(jìn)入非運(yùn)行態(tài)。 ①suspend()方法被調(diào)用;②sleep()方法被調(diào)用;③線程使用wait()來等待條件變量;④線程處于I/O等待。 ●死亡態(tài)(Dead)當(dāng)run()方法返回,或別的線程調(diào)用stop()方法,線程進(jìn)入死亡態(tài) 。 通常Applet使用它的stop()方法來終止它產(chǎn)生的所有線程。 (3)線程優(yōu)先級雖然我們說線程是并發(fā)運(yùn)行的。 然而事實(shí)常常并非如此。 正如前面談到的,當(dāng)系統(tǒng)中只有一個CPU時,以某種順序在單CPU情況下執(zhí)行多線程被稱為調(diào)度(scheduling)。 Java采用的是一種簡單、固定的調(diào)度法,即固定優(yōu)先級調(diào)度。 這種算法是根據(jù)處于可運(yùn)行態(tài)線程的相對優(yōu)先級來實(shí)行調(diào)度。 當(dāng)線程產(chǎn)生時,它繼承原線程的優(yōu)先級。 在需要時可對優(yōu)先級進(jìn)行修改。 在任何時刻,如果有多條線程等待運(yùn)行,系統(tǒng)選擇優(yōu)先級最高的可運(yùn)行線程運(yùn)行。 只有當(dāng)它停止、自動放棄、或由于某種原因成為非運(yùn)行態(tài)低優(yōu)先級的線程才能運(yùn)行。 如果兩個線程具有相同的優(yōu)先級,它們將被交替地運(yùn)行。 Java實(shí)時系統(tǒng)的線程調(diào)度算法還是強(qiáng)制性的,在任何時刻,如果一個比其他線程優(yōu)先級都高的線程的狀態(tài)變?yōu)榭蛇\(yùn)行態(tài),實(shí)時系統(tǒng)將選擇該線程來運(yùn)行。 (4)幽靈線程任何一個Java線程都能成為幽靈線程。 它是作為運(yùn)行于同一個進(jìn)程內(nèi)的對象和線程的服務(wù)提供者。 例如,HotJava瀏覽器有一個稱為 后臺圖片閱讀器的幽靈線程,它為需要圖片的對象和線程從文件系統(tǒng)或網(wǎng)絡(luò)讀入圖片。 幽靈線程是應(yīng)用中典型的獨(dú)立線程。 它為同一應(yīng)用中的其他對象和線程提供服務(wù)。 幽靈線程的run()方法一般都是無限循環(huán),等待服務(wù)請求。 (5)線程組每個Java線程都是某個線程組的成員。 線程組提供一種機(jī)制,使得多個線程集于一個對象內(nèi),能對它們實(shí)行整體操作。 譬如,你能用一個方法調(diào)用來啟動或掛起組內(nèi)的所有線程。 Java線程組由ThreadGroup類實(shí)現(xiàn)。 當(dāng)線程產(chǎn)生時,可以指定線程組或由實(shí)時系統(tǒng)將其放入某個缺省的線程組內(nèi)。 線程只能屬于一個線程組,并且當(dāng)線程產(chǎn)生后不能改變它所屬的線程組。 三、多線程程序?qū)τ诙嗑€程的好處這就不多說了。 但是,它同樣也帶來了某些新的麻煩。 只要在設(shè)計(jì)程序時特別小心留意,克服這些麻煩并不算太困難。 (1)同步線程許多線程在執(zhí)行中必須考慮與其他線程之間共享數(shù)據(jù)或協(xié)調(diào)執(zhí)行狀態(tài)。 這就需要同步機(jī)制。 在Java中每個對象都有一把鎖與之對應(yīng)。 但Java不提供單獨(dú)的lock和unlock操作。 它由高層的結(jié)構(gòu)隱式實(shí)現(xiàn), 來保證操作的對應(yīng)。 (然而,我們注意到Java虛擬機(jī)提供單獨(dú)的monito renter和monitorexit指令來實(shí)現(xiàn)lock和unlock操作。 )synchronized語句計(jì)算一個對象引用,試圖對該對象完成鎖操作, 并且在完成鎖操作前停止處理。 當(dāng)鎖操作完成synchronized語句體得到執(zhí)行。 當(dāng)語句體執(zhí)行完畢(無論正常或異常),解鎖操作自動完成。 作為面向?qū)ο蟮恼Z言,synchronized經(jīng)常與方法連用。 一種比較好的辦法是,如果某個變量由一個線程賦值并由別的線程引用或賦值,那么所有對該變量的訪問都必須在某個synchromized語句或synchronized方法內(nèi)。 現(xiàn)在假設(shè)一種情況:線程1與線程2都要訪問某個數(shù)據(jù)區(qū),并且要求線程1的訪問先于線程2, 則這時僅用synchronized是不能解決問題的。 這在Unix或Windows NT中可用Simaphore來實(shí)現(xiàn)。 而Java并不提供。 在Java中提供的是wait()和notify()機(jī)制。 使用如下:synchronized method-1(…){ call by thread 1.‖access data area;available=true;notify()}synchronized method-2(…){‖call by thread (!available)try{wait();‖wait for notify().}catch (Interrupted Exception e){}‖access data area}其中available是類成員變量,置初值為false。 如果在method-2中檢查available為假,則調(diào)用wait()。 wait()的作用是使線程2進(jìn)入非運(yùn)行態(tài),并且解鎖。 在這種情況下,method-1可以被線程1調(diào)用。 當(dāng)執(zhí)行notify()后。 線程2由非運(yùn)行態(tài)轉(zhuǎn)變?yōu)榭蛇\(yùn)行態(tài)。 當(dāng)method-1調(diào)用返回后。 線程2可重新對該對象加鎖,加鎖成功后執(zhí)行wait()返回后的指令。 這種機(jī)制也能適用于其他更復(fù)雜的情況。 (2)死鎖如果程序中有幾個競爭資源的并發(fā)線程,那么保證均衡是很重要的。 系統(tǒng)均衡是指每個線程在執(zhí)行過程中都能充分訪問有限的資源。 系統(tǒng)中沒有餓死和死鎖的線程。 Java并不提供對死鎖的檢測機(jī)制。 對大多數(shù)的Java程序員來說防止死鎖是一種較好的選擇。 最簡單的防止死鎖的方法是對競爭的資源引入序號,如果一個線程需要幾個資源,那么它必須先得到小序號的資源,再申請大序號的資源。 四、線程和進(jìn)程的比較進(jìn)程是資源分配的基本單位。 所有與該進(jìn)程有關(guān)的資源,都被記錄在進(jìn)程控制塊PCB中。 以表示該進(jìn)程擁有這些資源或正在使用它們。 另外,進(jìn)程也是搶占處理機(jī)的調(diào)度單位,它擁有一個完整的虛擬地址空間。 與進(jìn)程相對應(yīng),線程與資源分配無關(guān),它屬于某一個進(jìn)程,并與進(jìn)程內(nèi)的其他線程一起共享進(jìn)程的資源。 當(dāng)進(jìn)程發(fā)生調(diào)度時,不同的進(jìn)程擁有不同的虛擬地址空間,而同一進(jìn)程內(nèi)的不同線程共享同一地址空間。 線程只由相關(guān)堆棧(系統(tǒng)棧或用戶棧)寄存器和線程控制表TCB組成。 寄存器可被用來存儲線程內(nèi)的局部變量,但不能存儲其他線程的相關(guān)變量。 發(fā)生進(jìn)程切換與發(fā)生線程切換時相比較,進(jìn)程切換時涉及到有關(guān)資源指針的保存以及地址空間的變化等問題;線程切換時,由于同不進(jìn)程內(nèi)的線程共享資源和地址 空間,將不涉及資源信息的保存和地址變化問題,從而減少了操作系統(tǒng)的開銷時間。 而且,進(jìn)程的調(diào)度與切換都是由操作系統(tǒng)內(nèi)核完成,而線程則既可由操作系統(tǒng)內(nèi) 核完成,也可由用戶程序進(jìn)行。 五、線程的適用范圍典型的應(yīng)用 1.服務(wù)器中的文件管理或通信控制 2.前后臺處理 3.異步處理六、線程的執(zhí)行特性一個線程必須處于如下四種可能的狀態(tài)之一:初始態(tài):一個線程調(diào)用了new方法之后,并在調(diào)用start方法之前的所處狀態(tài)。 在初始態(tài)中,可以調(diào)用start和stop方法。 Runnable:一旦線程調(diào)用了start 方法,線程就轉(zhuǎn)到Runnable 狀態(tài),注意,如果線程處于Runnable狀態(tài),它也有可能不在運(yùn)行,這是因?yàn)檫€有優(yōu)先級和調(diào)度問題。 阻塞/ NonRunnable:線程處于阻塞/NonRunnable狀態(tài),這是由兩種可能性造成的:要么是因掛起而暫停的,要么是由于某些原因而阻塞的,例如包括等待IO請求的完成。 退出:線程轉(zhuǎn)到退出狀態(tài),這有兩種可能性,要么是run方法執(zhí)行結(jié)束,要么是調(diào)用了stop方法。 最后一個概念就是線程的優(yōu)先級,線程可以設(shè)定優(yōu)先級,高優(yōu)先級的線程可以安排在低優(yōu)先級線程之前完成。 一個應(yīng)用程序可以通過使用線程中的方法setPriority(int),來設(shè)置線程的優(yōu)先級大小。 線程有5種基本操作: 派生:線程在進(jìn)程內(nèi)派生出來,它即可由進(jìn)程派生,也可由線程派生。 阻塞(Block):如果一個線程在執(zhí)行過程中需要等待某個事件發(fā)生,則被阻塞。 激活(unblock):如果阻塞線程的事件發(fā)生,則該線程被激活并進(jìn)入就緒隊(duì)列。 調(diào)度(schedule):選擇一個就緒線程進(jìn)入執(zhí)行狀態(tài)。 結(jié)束(Finish):如果一個線程執(zhí)行結(jié)束,它的寄存器上下文以及堆棧內(nèi)容等將被釋放。 七、線程的分類線程有兩個基本類型: 用戶級線程:管理過程全部由用戶程序完成,操作系統(tǒng)內(nèi)核心只對進(jìn)程進(jìn)行管理。 系統(tǒng)級線程(核心級線程):由操作系統(tǒng)內(nèi)核進(jìn)行管理。 操作系統(tǒng)內(nèi)核給應(yīng)用程序提供相應(yīng)的系統(tǒng)調(diào)用和應(yīng)用程序接口API,以使用戶程序可以創(chuàng)建、執(zhí)行、撤消線程。 附:線程舉例1. SUN Solaris 2.3Solaris支持內(nèi)核線程、輕權(quán)進(jìn)程和用戶線程。 一個進(jìn)程可有大量用戶線程;大量用戶線程復(fù)用少量的輕權(quán)進(jìn)程,輕權(quán)進(jìn)程與內(nèi)核線程一一對應(yīng)。 用戶級線程在調(diào)用核心服務(wù)時(如文件讀寫),需要“捆綁(bound)”在一個LWP上。 永久捆綁(一個LWP固定被一個用戶級線程占用,該LWP移到LWP池之外)和臨時捆綁(從LWP池中臨時分配一個未被占用的LWP)。 在調(diào)用系統(tǒng)服務(wù)時,如果所有LWP已被其他用戶級線程所占用(捆綁),則該線程阻塞直到有可用的LWP。 如果LWP執(zhí)行系統(tǒng)線程時阻塞(如read()調(diào)用),則當(dāng)前捆綁在LWP上的用戶級線程也阻塞。 ¨有關(guān)的C庫函數(shù)/* 創(chuàng)建用戶級線程 */int thr_create(void *stack_base, size_t stack_size,void *(*start_routine)(void *), void *arg, long flags,thread_t *new_thread_id);其中flags包括:THR_BOUND(永久捆綁), THR_NEW_LWP(創(chuàng)建新LWP放入LWP池),若兩者同時指定則創(chuàng)建兩個新LWP,一個永久捆綁而另一個放入LWP池。 2有關(guān)的系統(tǒng)調(diào)用 /* 在當(dāng)前進(jìn)程中創(chuàng)建LWP */int _lwp_create(ucontext_t *contextp, unsigned long flags,lwpid_t *new_lwp_id);/* 構(gòu)造LWP上下文*/void _lwp_makecontext(ucontext_t *ucp, void (*start_routine)( void *), void *arg,void *private, caddr_t stack_base, size_t stack_size);/* 注意:沒有進(jìn)行“捆綁”操作的系統(tǒng)調(diào)用 */2. Windows NTNT線程的上下文包括:寄存器、核心棧、線程環(huán)境塊和用戶棧。 NT線程狀態(tài) (1) 就緒狀態(tài):進(jìn)程已獲得除處理機(jī)外的所需資源,等待執(zhí)行。 (2) 備用狀態(tài):特定處理器的執(zhí)行對象,系統(tǒng)中每個處理器上只能有一個處于備用狀態(tài)的線程。 (3) 運(yùn)行狀態(tài):完成描述表切換,線程進(jìn)入運(yùn)行狀態(tài),直到內(nèi)核搶先、時間片用完、線程終止或進(jìn)行等待狀態(tài)。 (4) 等待狀態(tài):線程等待對象句柄,以同步它的執(zhí)行。 等待結(jié)束時,根據(jù)優(yōu)先級進(jìn)入運(yùn)行、就緒狀態(tài)。 (5) 轉(zhuǎn)換狀態(tài):線程在準(zhǔn)備執(zhí)行而其內(nèi)核堆棧處于外存時,線程進(jìn)入轉(zhuǎn)換狀態(tài);當(dāng)其內(nèi)核堆棧調(diào)回內(nèi)存,線程進(jìn)入就緒狀態(tài)。 (6) 終止?fàn)顟B(tài):線程執(zhí)行完就進(jìn)入終止?fàn)顟B(tài);如執(zhí)行體有一指向線程對象的指針,可將線程對象重新初始化,并再次使用。 NT線程的有關(guān)APICreateThread()函數(shù)在調(diào)用進(jìn)程的地址空間上創(chuàng)建一個線程,以執(zhí)行指定的函數(shù);返回值為所創(chuàng)建線程的句柄。 ExitThread()函數(shù)用于結(jié)束本線程。 SuspendThread()函數(shù)用于掛起指定的線程。 ResumeThread()函數(shù)遞減指定線程的掛起計(jì)數(shù),掛起計(jì)數(shù)為0時,線程恢復(fù)執(zhí)行。
一、線程的概念一般來說,我們把正在計(jì)算機(jī)中執(zhí)行的程序叫做進(jìn)程(Process) ,而不將其稱為程序(Program)。 所謂線程(Thread),是進(jìn)程中某個單一順序的控制流。 新興的操作系統(tǒng),如Mac,Windows NT,Windows 95等,大多采用多線程的概念,把線程視為基本執(zhí)行單位。 線程也是Java中的相當(dāng)重要的組成部分之一。 甚至最簡單的Applet也是由多個線程來完成的。 在Java中,任何一個Applet的paint()和update()方法都是由AWT(Abstract Window Toolkit)繪圖與事件處理線程調(diào)用的,而Applet 主要的里程碑方法——init(),start(),stop()和destory()——是由執(zhí)行該Applet的應(yīng)用調(diào)用的。 單線程的概念沒有什么新的地方,真正有趣的是在一個程序中同時使用多個線程來完成不同的任務(wù)。 某些地方用輕量進(jìn)程(Lightweig ht Process)來代替線程,線程與真正進(jìn)程的相似性在于它們都是單一順序控制流。 然而線程被認(rèn)為輕量是由于它運(yùn)行于整個程序的上下文內(nèi),能使用整個程序共有的資源和程序環(huán)境。 作為單一順序控制流,在運(yùn)行的程序內(nèi)線程必須擁有一些資源作為必要的開銷。 例如,必須有執(zhí)行堆棧和程序計(jì)數(shù)器。 在線程內(nèi)執(zhí)行的代碼只在它的上下文中起作用,因此某些地方用執(zhí)行上下文來代替線程。 二、線程屬性為了正確有效地使用線程,必須理解線程的各個方面并了解Java 實(shí)時系統(tǒng)。 必須知道如何提供線程體、線程的生命周期、實(shí)時系統(tǒng)如 何調(diào)度線程、線程組、什么是幽靈線程(Demo nThread)。 (1)線程體所有的操作都發(fā)生在線程體中,在Java中線程體是從Thread類繼承的run()方法,或?qū)崿F(xiàn)Runnable接口的類中的run()方法。 當(dāng)線程產(chǎn)生并初始化后,實(shí)時系統(tǒng)調(diào)用它的run()方法。 run()方法內(nèi)的代碼實(shí)現(xiàn)所產(chǎn)生線程的行為,它是線程的主要部分。 (2)線程狀態(tài)附圖表示了線程在它的生命周期內(nèi)的任何時刻所能處的狀態(tài)以及引起狀態(tài)改變的方法。 這圖并不是完整的有限狀態(tài)圖,但基本概括了線程中比較感興趣和普遍的方面。 以下討論有關(guān)線程生命周期以此為據(jù)。 ●新線程態(tài)(New Thread)產(chǎn)生一個Thread對象就生成一個新線程。 當(dāng)線程處于新線程狀態(tài)時,僅僅是一個空線程對象,它還沒有分配到系統(tǒng)資源。 因此只能啟動或終止它。 任何其他操作都會引發(fā)異常。 ●可運(yùn)行態(tài)(Runnable)start()方法產(chǎn)生運(yùn)行線程所必須的資源,調(diào)度線程執(zhí)行,并且調(diào)用線程的run()方法。 在這時線程處于可運(yùn)行態(tài)。 該狀態(tài)不稱為運(yùn)行態(tài)是因?yàn)檫@時的線程并不總是一直占用處理機(jī)。 特別是對于只有一個處理機(jī)的PC而言,任何時刻只能有一個處于可運(yùn)行態(tài)的線程占用處理 機(jī)。 Java通過調(diào)度來實(shí)現(xiàn)多線程對處理機(jī)的共享。 ●非運(yùn)行態(tài)(Not Runnable)當(dāng)以下事件發(fā)生時,線程進(jìn)入非運(yùn)行態(tài)。 ①suspend()方法被調(diào)用;②sleep()方法被調(diào)用;③線程使用wait()來等待條件變量;④線程處于I/O等待。 ●死亡態(tài)(Dead)當(dāng)run()方法返回,或別的線程調(diào)用stop()方法,線程進(jìn)入死亡態(tài) 。 通常Applet使用它的stop()方法來終止它產(chǎn)生的所有線程。 (3)線程優(yōu)先級雖然我們說線程是并發(fā)運(yùn)行的。 然而事實(shí)常常并非如此。 正如前面談到的,當(dāng)系統(tǒng)中只有一個CPU時,以某種順序在單CPU情況下執(zhí)行多線程被稱為調(diào)度(scheduling)。 Java采用的是一種簡單、固定的調(diào)度法,即固定優(yōu)先級調(diào)度。 這種算法是根據(jù)處于可運(yùn)行態(tài)線程的相對優(yōu)先級來實(shí)行調(diào)度。 當(dāng)線程產(chǎn)生時,它繼承原線程的優(yōu)先級。 在需要時可對優(yōu)先級進(jìn)行修改。 在任何時刻,如果有多條線程等待運(yùn)行,系統(tǒng)選擇優(yōu)先級最高的可運(yùn)行線程運(yùn)行。 只有當(dāng)它停止、自動放棄、或由于某種原因成為非運(yùn)行態(tài)低優(yōu)先級的線程才能運(yùn)行。 如果兩個線程具有相同的優(yōu)先級,它們將被交替地運(yùn)行。 Java實(shí)時系統(tǒng)的線程調(diào)度算法還是強(qiáng)制性的,在任何時刻,如果一個比其他線程優(yōu)先級都高的線程的狀態(tài)變?yōu)榭蛇\(yùn)行態(tài),實(shí)時系統(tǒng)將選擇該線程來運(yùn)行。 (4)幽靈線程任何一個Java線程都能成為幽靈線程。 它是作為運(yùn)行于同一個進(jìn)程內(nèi)的對象和線程的服務(wù)提供者。 例如,HotJava瀏覽器有一個稱為 后臺圖片閱讀器的幽靈線程,它為需要圖片的對象和線程從文件系統(tǒng)或網(wǎng)絡(luò)讀入圖片。 幽靈線程是應(yīng)用中典型的獨(dú)立線程。 它為同一應(yīng)用中的其他對象和線程提供服務(wù)。 幽靈線程的run()方法一般都是無限循環(huán),等待服務(wù)請求。 (5)線程組每個Java線程都是某個線程組的成員。 線程組提供一種機(jī)制,使得多個線程集于一個對象內(nèi),能對它們實(shí)行整體操作。 譬如,你能用一個方法調(diào)用來啟動或掛起組內(nèi)的所有線程。 Java線程組由ThreadGroup類實(shí)現(xiàn)。 當(dāng)線程產(chǎn)生時,可以指定線程組或由實(shí)時系統(tǒng)將其放入某個缺省的線程組內(nèi)。 線程只能屬于一個線程組,并且當(dāng)線程產(chǎn)生后不能改變它所屬的線程組。 三、多線程程序?qū)τ诙嗑€程的好處這就不多說了。 但是,它同樣也帶來了某些新的麻煩。 只要在設(shè)計(jì)程序時特別小心留意,克服這些麻煩并不算太困難。 (1)同步線程許多線程在執(zhí)行中必須考慮與其他線程之間共享數(shù)據(jù)或協(xié)調(diào)執(zhí)行狀態(tài)。 這就需要同步機(jī)制。 在Java中每個對象都有一把鎖與之對應(yīng)。 但Java不提供單獨(dú)的lock和unlock操作。 它由高層的結(jié)構(gòu)隱式實(shí)現(xiàn), 來保證操作的對應(yīng)。 (然而,我們注意到Java虛擬機(jī)提供單獨(dú)的monito renter和monitorexit指令來實(shí)現(xiàn)lock和unlock操作。 )synchronized語句計(jì)算一個對象引用,試圖對該對象完成鎖操作, 并且在完成鎖操作前停止處理。 當(dāng)鎖操作完成synchronized語句體得到執(zhí)行。 當(dāng)語句體執(zhí)行完畢(無論正常或異常),解鎖操作自動完成。 作為面向?qū)ο蟮恼Z言,synchronized經(jīng)常與方法連用。 一種比較好的辦法是,如果某個變量由一個線程賦值并由別的線程引用或賦值,那么所有對該變量的訪問都必須在某個synchromized語句或synchronized方法內(nèi)。 現(xiàn)在假設(shè)一種情況:線程1與線程2都要訪問某個數(shù)據(jù)區(qū),并且要求線程1的訪問先于線程2, 則這時僅用synchronized是不能解決問題的。 這在Unix或Windows NT中可用Simaphore來實(shí)現(xiàn)。 而Java并不提供。 在Java中提供的是wait()和notify()機(jī)制。 使用如下:synchronized method-1(…){ call by thread 1.‖access data area;available=true;notify()}synchronized method-2(…){‖call by thread (!available)try{wait();‖wait for notify().}catch (Interrupted Exception e){}‖access data area}其中available是類成員變量,置初值為false。 如果在method-2中檢查available為假,則調(diào)用wait()。 wait()的作用是使線程2進(jìn)入非運(yùn)行態(tài),并且解鎖。 在這種情況下,method-1可以被線程1調(diào)用。 當(dāng)執(zhí)行notify()后。 線程2由非運(yùn)行態(tài)轉(zhuǎn)變?yōu)榭蛇\(yùn)行態(tài)。 當(dāng)method-1調(diào)用返回后。 線程2可重新對該對象加鎖,加鎖成功后執(zhí)行wait()返回后的指令。 這種機(jī)制也能適用于其他更復(fù)雜的情況。 (2)死鎖如果程序中有幾個競爭資源的并發(fā)線程,那么保證均衡是很重要的。 系統(tǒng)均衡是指每個線程在執(zhí)行過程中都能充分訪問有限的資源。 系統(tǒng)中沒有餓死和死鎖的線程。 Java并不提供對死鎖的檢測機(jī)制。 對大多數(shù)的Java程序員來說防止死鎖是一種較好的選擇。 最簡單的防止死鎖的方法是對競爭的資源引入序號,如果一個線程需要幾個資源,那么它必須先得到小序號的資源,再申請大序號的資源。 四、線程和進(jìn)程的比較進(jìn)程是資源分配的基本單位。 所有與該進(jìn)程有關(guān)的資源,都被記錄在進(jìn)程控制塊PCB中。 以表示該進(jìn)程擁有這些資源或正在使用它們。 另外,進(jìn)程也是搶占處理機(jī)的調(diào)度單位,它擁有一個完整的虛擬地址空間。 與進(jìn)程相對應(yīng),線程與資源分配無關(guān),它屬于某一個進(jìn)程,并與進(jìn)程內(nèi)的其他線程一起共享進(jìn)程的資源。 當(dāng)進(jìn)程發(fā)生調(diào)度時,不同的進(jìn)程擁有不同的虛擬地址空間,而同一進(jìn)程內(nèi)的不同線程共享同一地址空間。 線程只由相關(guān)堆棧(系統(tǒng)棧或用戶棧)寄存器和線程控制表TCB組成。 寄存器可被用來存儲線程內(nèi)的局部變量,但不能存儲其他線程的相關(guān)變量。 發(fā)生進(jìn)程切換與發(fā)生線程切換時相比較,進(jìn)程切換時涉及到有關(guān)資源指針的保存以及地址空間的變化等問題;線程切換時,由于同不進(jìn)程內(nèi)的線程共享資源和地址 空間,將不涉及資源信息的保存和地址變化問題,從而減少了操作系統(tǒng)的開銷時間。 而且,進(jìn)程的調(diào)度與切換都是由操作系統(tǒng)內(nèi)核完成,而線程則既可由操作系統(tǒng)內(nèi) 核完成,也可由用戶程序進(jìn)行。 五、線程的適用范圍典型的應(yīng)用 1.服務(wù)器中的文件管理或通信控制 2.前后臺處理 3.異步處理六、線程的執(zhí)行特性一個線程必須處于如下四種可能的狀態(tài)之一:初始態(tài):一個線程調(diào)用了new方法之后,并在調(diào)用start方法之前的所處狀態(tài)。 在初始態(tài)中,可以調(diào)用start和stop方法。 Runnable:一旦線程調(diào)用了start 方法,線程就轉(zhuǎn)到Runnable 狀態(tài),注意,如果線程處于Runnable狀態(tài),它也有可能不在運(yùn)行,這是因?yàn)檫€有優(yōu)先級和調(diào)度問題。 阻塞/ NonRunnable:線程處于阻塞/NonRunnable狀態(tài),這是由兩種可能性造成的:要么是因掛起而暫停的,要么是由于某些原因而阻塞的,例如包括等待IO請求的完成。 退出:線程轉(zhuǎn)到退出狀態(tài),這有兩種可能性,要么是run方法執(zhí)行結(jié)束,要么是調(diào)用了stop方法。 最后一個概念就是線程的優(yōu)先級,線程可以設(shè)定優(yōu)先級,高優(yōu)先級的線程可以安排在低優(yōu)先級線程之前完成。 一個應(yīng)用程序可以通過使用線程中的方法setPriority(int),來設(shè)置線程的優(yōu)先級大小。 線程有5種基本操作: 派生:線程在進(jìn)程內(nèi)派生出來,它即可由進(jìn)程派生,也可由線程派生。 阻塞(Block):如果一個線程在執(zhí)行過程中需要等待某個事件發(fā)生,則被阻塞。 激活(unblock):如果阻塞線程的事件發(fā)生,則該線程被激活并進(jìn)入就緒隊(duì)列。 調(diào)度(schedule):選擇一個就緒線程進(jìn)入執(zhí)行狀態(tài)。 結(jié)束(Finish):如果一個線程執(zhí)行結(jié)束,它的寄存器上下文以及堆棧內(nèi)容等將被釋放。 七、線程的分類線程有兩個基本類型: 用戶級線程:管理過程全部由用戶程序完成,操作系統(tǒng)內(nèi)核心只對進(jìn)程進(jìn)行管理。 系統(tǒng)級線程(核心級線程):由操作系統(tǒng)內(nèi)核進(jìn)行管理。 操作系統(tǒng)內(nèi)核給應(yīng)用程序提供相應(yīng)的系統(tǒng)調(diào)用和應(yīng)用程序接口API,以使用戶程序可以創(chuàng)建、執(zhí)行、撤消線程。 附:線程舉例1. SUN Solaris 2.3Solaris支持內(nèi)核線程、輕權(quán)進(jìn)程和用戶線程。 一個進(jìn)程可有大量用戶線程;大量用戶線程復(fù)用少量的輕權(quán)進(jìn)程,輕權(quán)進(jìn)程與內(nèi)核線程一一對應(yīng)。 用戶級線程在調(diào)用核心服務(wù)時(如文件讀寫),需要“捆綁(bound)”在一個LWP上。 永久捆綁(一個LWP固定被一個用戶級線程占用,該LWP移到LWP池之外)和臨時捆綁(從LWP池中臨時分配一個未被占用的LWP)。 在調(diào)用系統(tǒng)服務(wù)時,如果所有LWP已被其他用戶級線程所占用(捆綁),則該線程阻塞直到有可用的LWP。 如果LWP執(zhí)行系統(tǒng)線程時阻塞(如read()調(diào)用),則當(dāng)前捆綁在LWP上的用戶級線程也阻塞。 ¨有關(guān)的C庫函數(shù)/* 創(chuàng)建用戶級線程 */int thr_create(void *stack_base, size_t stack_size,void *(*start_routine)(void *), void *arg, long flags,thread_t *new_thread_id);其中flags包括:THR_BOUND(永久捆綁), THR_NEW_LWP(創(chuàng)建新LWP放入LWP池),若兩者同時指定則創(chuàng)建兩個新LWP,一個永久捆綁而另一個放入LWP池。 2有關(guān)的系統(tǒng)調(diào)用 /* 在當(dāng)前進(jìn)程中創(chuàng)建LWP */int _lwp_create(ucontext_t *contextp, unsigned long flags,lwpid_t *new_lwp_id);/* 構(gòu)造LWP上下文*/void _lwp_makecontext(ucontext_t *ucp, void (*start_routine)( void *), void *arg,void *private, caddr_t stack_base, size_t stack_size);/* 注意:沒有進(jìn)行“捆綁”操作的系統(tǒng)調(diào)用 */2. Windows NTNT線程的上下文包括:寄存器、核心棧、線程環(huán)境塊和用戶棧。 NT線程狀態(tài) (1) 就緒狀態(tài):進(jìn)程已獲得除處理機(jī)外的所需資源,等待執(zhí)行。 (2) 備用狀態(tài):特定處理器的執(zhí)行對象,系統(tǒng)中每個處理器上只能有一個處于備用狀態(tài)的線程。 (3) 運(yùn)行狀態(tài):完成描述表切換,線程進(jìn)入運(yùn)行狀態(tài),直到內(nèi)核搶先、時間片用完、線程終止或進(jìn)行等待狀態(tài)。 (4) 等待狀態(tài):線程等待對象句柄,以同步它的執(zhí)行。 等待結(jié)束時,根據(jù)優(yōu)先級進(jìn)入運(yùn)行、就緒狀態(tài)。 (5) 轉(zhuǎn)換狀態(tài):線程在準(zhǔn)備執(zhí)行而其內(nèi)核堆棧處于外存時,線程進(jìn)入轉(zhuǎn)換狀態(tài);當(dāng)其內(nèi)核堆棧調(diào)回內(nèi)存,線程進(jìn)入就緒狀態(tài)。 (6) 終止?fàn)顟B(tài):線程執(zhí)行完就進(jìn)入終止?fàn)顟B(tài);如執(zhí)行體有一指向線程對象的指針,可將線程對象重新初始化,并再次使用。 NT線程的有關(guān)APICreateThread()函數(shù)在調(diào)用進(jìn)程的地址空間上創(chuàng)建一個線程,以執(zhí)行指定的函數(shù);返回值為所創(chuàng)建線程的句柄。 ExitThread()函數(shù)用于結(jié)束本線程。 SuspendThread()函數(shù)用于掛起指定的線程。 ResumeThread()函數(shù)遞減指定線程的掛起計(jì)數(shù),掛起計(jì)數(shù)為0時,線程恢復(fù)執(zhí)行。
線程中sleep和wait的區(qū)別如下:一,首先二者的不同點(diǎn):1.這兩個方法來自不同的類分別是Thread和Object。 首先對于sleep()方法,要知道該方法是屬于Thread類中的。 而wait()方法,則是屬于Object類中的。 ()方法導(dǎo)致了程序暫停執(zhí)行指定的時間,讓出cpu該其他線程,但是他的監(jiān)控狀態(tài)依然保持者,當(dāng)指定的時間到了又會自動恢復(fù)運(yùn)行狀態(tài)。 在調(diào)用sleep()方法的過程中,線程不會釋放對象鎖。 而當(dāng)調(diào)用wait()方法的時候,線程會放棄對象鎖,進(jìn)入等待此對象的等待鎖定池,只有針對此對象調(diào)用notify()方法后本線程才進(jìn)入對象鎖定池準(zhǔn)備。 獲取對象鎖進(jìn)入運(yùn)行狀態(tài)。 3.最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。 wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用(使用范圍)比如:synchronized(x){()//或者wait()}必須捕獲異常,而wait,notify和notifyAll不需要捕獲異常舉例說明如下:1.首先我們先看sleep中的構(gòu)造函數(shù)sleep(long millis) Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and (long millis, int nanos)Causes the currently executing thread to sleep (cease execution) for the specified number of milliseconds plus the specified number of nanoseconds, subject to the precision and accuracy of system timers and 方法屬于Thread類中方法,表示讓一個線程進(jìn)入睡眠狀態(tài),等待一定的時間之后,自動醒來進(jìn)入到可運(yùn)行狀態(tài),不會馬上進(jìn)入運(yùn)行狀態(tài),因?yàn)榫€程調(diào)度機(jī)制恢復(fù)線程的運(yùn)行也需要時間,一個線程對象調(diào)用了sleep方法之后,并不會釋放他所持有的所有對象鎖,所以也就不會影響其他進(jìn)程對象的運(yùn)行。 但在sleep的過程中過程中有可能被其他對象調(diào)用它的interrupt(),產(chǎn)生InterruptedException異常,如果你的程序不捕獲這個異常,線程就會異常終止,進(jìn)入TERMINATED狀態(tài),如果你的程序捕獲了這個異常,那么程序就會繼續(xù)執(zhí)行catch語句塊(可能還有finally語句塊)以及以后的代碼。 另外注意sleep()方法是一個靜態(tài)方法,也就是說他只對當(dāng)前對象有效,通過()讓t對象進(jìn)入sleep,這樣的做法是錯誤的,它只會是使當(dāng)前線程被sleep 而不是t線程方法void wait(long timeout)Causes the current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has wait(long timeout, int nanos)Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object, or some other thread interrupts the current thread, or a certain amount of real time has 屬于Object的成員方法,一旦一個對象調(diào)用了wait方法,必須要采用notify()和notifyAll()方法喚醒該進(jìn)程;如果線程擁有某個或某些對象的同步鎖,那么在調(diào)用了wait()后,這個線程就會釋放它持有的所有同步資源,而不限于這個被調(diào)用了wait()方法的對象。 wait()方法也同樣會在wait的過程中有可能被其他對象調(diào)用interrupt()方法而產(chǎn)生InterruptedException,效果以及處理方式同sleep()方法二,最后二者的共同點(diǎn):1. 他們都是在多線程的環(huán)境下,都可以在程序的調(diào)用處阻塞指定的毫秒數(shù),并返回。 2. wait()和sleep()都可以通過interrupt()方法 打斷線程的暫停狀態(tài) ,從而使線程立刻拋出InterruptedException。 如果線程A希望立即結(jié)束線程B,則可以對線程B對應(yīng)的Thread實(shí)例調(diào)用interrupt方法。 如果此刻線程B正在wait/sleep /join,則線程B會立刻拋出InterruptedException,在catch() {} 中直接return即可安全地結(jié)束線程。 需要注意的是,InterruptedException是線程自己從內(nèi)部拋出的,并不是interrupt()方法拋出的。 對某一線程調(diào)用 interrupt()時,如果該線程正在執(zhí)行普通的代碼,那么該線程根本就不會拋出InterruptedException。 但是,一旦該線程進(jìn)入到 wait()/sleep()/join()后,就會立刻拋出InterruptedException 。 以上就是線程中sleep和wait的區(qū)別。
內(nèi)容聲明:
1、本站收錄的內(nèi)容來源于大數(shù)據(jù)收集,版權(quán)歸原網(wǎng)站所有!
2、本站收錄的內(nèi)容若侵害到您的利益,請聯(lián)系我們進(jìn)行刪除處理!
3、本站不接受違法信息,如您發(fā)現(xiàn)違法內(nèi)容,請聯(lián)系我們進(jìn)行舉報(bào)處理!
4、本文地址:http://www.lmxpnzry.com/article/e220f71e1ef2e9bf3c7a.html,復(fù)制請保留版權(quán)鏈接!
什么是GZip壓縮,GZip是一種文件壓縮算法,它可以減少文件的尺寸,同時保持其內(nèi)容的完整性,當(dāng)用于網(wǎng)站時,GZip可通過減少服務(wù)器和瀏覽器之間傳輸?shù)臄?shù)據(jù)量來提高加載速度,GZip壓縮的好處GZip壓縮為網(wǎng)站提供了諸多好處,包括,更快的加載速度,GZip壓縮減少了數(shù)據(jù)量,從而縮短了下載時間,提高了加載速度,提高帶寬利用率,GZip壓縮...。
互聯(lián)網(wǎng)資訊 2024-09-25 07:18:51
簡介Perl是一種高級、通用的、可移植的編程語言,以其數(shù)據(jù)處理、文本處理和系統(tǒng)管理能力而廣為人知,它由LarryWall于1987年創(chuàng)建,最初旨在處理UNIX文本文件,特性強(qiáng)大的文本處理,Perl具有強(qiáng)大的正則表達(dá)式支持,可用于輕松地操作和分析文本數(shù)據(jù),數(shù)據(jù)處理,Perl可以輕松處理復(fù)雜的數(shù)據(jù)結(jié)構(gòu),包括數(shù)組、哈希和列表,它還具有豐富的...。
技術(shù)教程 2024-09-17 05:17:49
varcanvas=document.getElementById,myCanvas,varctx=canvas.getContext,2d,創(chuàng)建一個矩形ctx.fillStyle=red,ctx.fillRect,10,10,100,50,添加一個事件監(jiān)聽器,當(dāng)鼠標(biāo)在矩形上移動時更改矩形的顏色canvas.addEve...。
技術(shù)教程 2024-09-16 21:52:25
在瞬息萬變的商業(yè)世界中,企業(yè)需要利用數(shù)據(jù)的力量來做出明智的決策并獲得競爭優(yōu)勢,云計(jì)算和大數(shù)據(jù)分析的融合已經(jīng)成為企業(yè)實(shí)現(xiàn)這一目標(biāo)的關(guān)鍵因素,云計(jì)算云計(jì)算是一種基于互聯(lián)網(wǎng)的計(jì)算模式,它允許企業(yè)使用遠(yuǎn)程服務(wù)器和資源來存儲、管理和處理數(shù)據(jù),云計(jì)算提供了幾個關(guān)鍵優(yōu)勢,包括,按需可擴(kuò)展性,云計(jì)算允許企業(yè)根據(jù)需要輕松地?cái)U(kuò)展或縮小其資源,從而避免過度...。
最新資訊 2024-09-15 16:04:40
隨著軟件開發(fā)變得越來越復(fù)雜,保持競爭力并按時交付出高質(zhì)量軟件至關(guān)重要,使用代碼生成器可以幫助您實(shí)現(xiàn)這些目標(biāo),什么是代碼生成器,代碼生成器是一種軟件工具,它可以根據(jù)指定規(guī)范自動生成代碼,這可以節(jié)省大量的時間和資源,因?yàn)樗耸謩泳幋a的需要,代碼生成器的優(yōu)勢提高生產(chǎn)力,代碼生成器可以大幅提高開發(fā)人員的生產(chǎn)力,因?yàn)樗鼈儨p少了編寫重復(fù)性或繁...。
互聯(lián)網(wǎng)資訊 2024-09-11 14:47:55
Java是一種廣泛使用的高級編程語言,用于開發(fā)各種應(yīng)用程序,從桌面軟件到移動應(yīng)用程序,它是現(xiàn)代編程中的基石,擁有龐大的開發(fā)者社區(qū)和大量的資源,黑馬Java培訓(xùn)優(yōu)勢系統(tǒng)化學(xué)習(xí),黑馬的Java課程按照循序漸進(jìn)的順序設(shè)計(jì),涵蓋了Java編程的基礎(chǔ)知識到高級概念,實(shí)戰(zhàn)項(xiàng)目驅(qū)動,您將通過動手實(shí)踐項(xiàng)目掌握J(rèn)ava技術(shù),解決實(shí)際問題并構(gòu)建自己的應(yīng)用...。
互聯(lián)網(wǎng)資訊 2024-09-11 11:40:19
常見問題解答如何下載PHP,您可以從PHP官方網(wǎng)站下載PHP,https,www.php.net,downloads,哪個PHP版本最穩(wěn)定,推薦使用PHP的最新穩(wěn)定版本,目前為8.2,優(yōu)化PHP性能使用緩存、壓縮和代碼優(yōu)化技術(shù)來提高PHP應(yīng)用程序的性能,注意,本文檔僅供參考,實(shí)際的下載、故障排除和最佳實(shí)踐可能根據(jù)您的系統(tǒng)配置和應(yīng)用...。
最新資訊 2024-09-10 17:58:24
隨著移動互聯(lián)網(wǎng)的快速發(fā)展,越來越多的用戶通過手機(jī)進(jìn)行搜索,為提升移動搜索體驗(yàn),百度搜索對移動端進(jìn)行了界面改版,并推出了多項(xiàng)優(yōu)化措施,本文將介紹百度搜索移動端優(yōu)化的技巧,幫助開發(fā)者和網(wǎng)站優(yōu)化人員提升移動搜索排名,獲取更多流量,一、百度搜索移動端界面改版百度搜索移動端界面改版后,更加簡潔、清晰,首頁重點(diǎn)突出搜索框,并新增了語音搜索、掃碼搜...。
互聯(lián)網(wǎng)資訊 2024-09-09 18:35:18
在當(dāng)今的信息時代,圖像已成為必不可少的交流方式,各大搜索引擎對圖像的優(yōu)化也日益重視,百度搜索也不例外,通過對圖像進(jìn)行優(yōu)化,我們可以讓我們的圖片在百度搜索中獲得更好的排名,從而獲得更多的流量和轉(zhuǎn)化,百度搜索圖像優(yōu)化要素在對圖像進(jìn)行優(yōu)化時,我們需要考慮以下幾個要素,1.圖片文件格式百度搜索支持多種圖片文件格式,包括JPEG、PNG和GIF...。
技術(shù)教程 2024-09-09 18:28:27
簡介對象導(dǎo)向編程,OOP,是計(jì)算機(jī)編程中的一種范式,它通過使用對象、類和繼承等概念來組織代碼,在VB中,OOP的實(shí)現(xiàn)稱為vbs對象,本文將探索vbs對象的強(qiáng)大功能,包括可重用性、封裝和繼承,可重用性vbs對象的一個主要優(yōu)點(diǎn)是其可重用性,創(chuàng)建一個對象后,可以將其多次用于不同的項(xiàng)目和應(yīng)用程序,這可以節(jié)省大量時間和精力,并確保代碼的連貫性,...。
最新資訊 2024-09-09 03:38:09
優(yōu)化Java源代碼是提升應(yīng)用程序性能的關(guān)鍵步驟,精心設(shè)計(jì)、清晰簡潔的代碼可以顯著提高應(yīng)用程序的效率,縮短響應(yīng)時間并減少資源消耗,優(yōu)化原則1.避免重復(fù)代碼重復(fù)的代碼不僅會浪費(fèi)資源,還有可能導(dǎo)致錯誤和不一致,使用方法和提取重復(fù)的代碼塊,保持代碼的可維護(hù)性和高效性,2.使用適當(dāng)?shù)臄?shù)據(jù)結(jié)構(gòu)選擇合適的數(shù)據(jù)結(jié)構(gòu)可以極大地影響性能,考慮數(shù)據(jù)的類型、...。
互聯(lián)網(wǎng)資訊 2024-09-05 22:14:52
引言JavaScript是一種強(qiáng)大的前端編程語言,為現(xiàn)代Web開發(fā)提供了豐富的功能,它使交互式網(wǎng)站、動態(tài)頁面和復(fù)雜應(yīng)用程序的開發(fā)成為可能,本文將深入探討JavaScript的神奇力量,揭示它如何徹底改變前端開發(fā),并幫助構(gòu)建令人驚嘆的Web體驗(yàn),互動性JavaScript最顯著的特性之一是其提供交互性的能力,以下是一些它如何讓網(wǎng)站變得活...。
最新資訊 2024-09-05 09:54:10