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