文章編號:11439時間:2024-09-30人氣:
在構建高性能、可擴展的 Java 應用程序時,充分利用多線程至關重要。如果不慎使用,線程也可能成為性能瓶頸,甚至導致不可預測的行為。
suspendThread 方法是一種過時的線程管理技術,在 Java 9 及更高版本中已被棄用。它通過暫停線程的執行來凍結線程,但在某些情況下可能導致死鎖和不可預測的行為。因此,在現代 Java 應用程序中使用 suspendThread 并不是一個好習慣。
有幾種現代技術可以用來高效地管理線程,避免了 suspendThread 的缺點。以下是一些常見的方法:
wait() 和 notify() 方法允許線程等待特定條件滿足后再繼續執行。這對于實現生產者-消費者模式和同步線程訪問共享資源非常有用。
Lock 和 Condition 類提供了一種更靈活的方式來管理線程同步。它們允許線程在獲取鎖后執行某些操作,并在特定條件滿足時釋放鎖。
CountDownLatch 類用于等待一組線程完成特定任務。它提供了一個計數器,在所有線程完成任務后減為零,從而允許主線程繼續執行。
Semaphore 類用于限制對共享資源的并發訪問。它允許指定資源的最大訪問數,并強制線程在訪問資源之前獲取許可證。
假設我們要構建一個簡單的生產者-消費者應用程序,其中生產者線程將數據放入隊列,而消費者線程將數據從隊列中取出。我們可以使用 Lock 和 Condition 如下實現它:
java import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Condition;一、線程的概念一般來說,我們把正在計算機中執行的程序叫做進程(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時,線程恢復執行。
線程的同步是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(); ,僅適用于Sun Solaris操作系統。
所以UNIX International線程也常被俗稱為Solaris線程。
1.創建線程intthr_create(void*stack_base,size_tstack_size,void*(*start_routine)(void*),void*arg,longflags,thread_t*new_thr);2.等待線程intthr_join(thread_twait_for,thread_t*dead,void**status);3.掛起線程intthr_suspend(thread_tthr);4.繼續線程intthr_continue(thread_tthr);5.退出線程voidthr_exit(void*status);6.返回當前線程的線程標識符thread_tthr_self(void);POSIX線程POSIX線程(Pthreads)的頭文件是
一、線程的概念一般來說,我們把正在計算機中執行的程序叫做進程(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時,線程恢復執行。
內容聲明:
1、本站收錄的內容來源于大數據收集,版權歸原網站所有!
2、本站收錄的內容若侵害到您的利益,請聯系我們進行刪除處理!
3、本站不接受違法信息,如您發現違法內容,請聯系我們進行舉報處理!
4、本文地址:http://www.lmxpnzry.com/article/b13e50c6f448876b4db8.html,復制請保留版權鏈接!
簡介多項式擬合是一種機器學習技術,用于擬合數據點到多項式曲線,它是一種強大的工具,可以用于各種應用,包括回歸分析、曲線擬合和預測,在本文中,我們將探討多項式擬合在機器學習和人工智能中的應用,我們將從理論基礎開始,然后討論實際應用以及使用多項式擬合的代碼示例,理論基礎多項式擬合的目標是找到一個多項式函數,它最能擬合一組數據點,對于給定的...。
本站公告 2024-09-29 19:16:44
隨著企業數據不斷增長,管理和優化存儲空間變得越來越具有挑戰性,磁盤配額是一種有用的工具,可以通過自動化存儲管理流程,為管理員節省大量時間和精力,什么是磁盤配額,磁盤配額是指為指定用戶或組分配的存儲空間限制,通過實施磁盤配額,管理員可以限制特定用戶或組可以使用的存儲量,這有助于防止單個用戶或組壟斷共享存儲池,并確保所有用戶公平地訪問存儲...。
技術教程 2024-09-25 12:06:29
DelphiXE5是一款用于構建桌面、移動和網絡應用程序的強大開發工具,它提供了豐富的功能和特性,使開發人員能夠創建高性能、可擴展且易于維護的應用程序,桌面應用程序DelphiXE5非常適合構建功能豐富且用戶友好的桌面應用程序,它支持各種控件和組件,包括按鈕、文本框、網格和圖表,開發人員可以使用直觀的拖放界面輕松地創建復雜的應用程序,...。
互聯網資訊 2024-09-24 09:53:13
前言在現代web開發中,多媒體內容扮演著越來越重要的角色,從視頻和音頻到交互式3D模型,豐富的多媒體體驗可以顯著提升用戶參與度和網站整體質量,為了在網頁上實現這些多媒體內容,網頁播放器發揮著至關重要的作用,網頁播放器是一個包含用戶界面和媒體播放功能的嵌入式組件,允許用戶輕松控制和觀看媒體內容,常見的網頁播放器技術HTML5媒體元素HT...。
技術教程 2024-09-15 17:17:18
簡介fprintf函數是C編程語言中用于格式化輸出的強大工具,它允許您以一種可控的方式將數據寫入文件或標準輸出流,通過使用格式化字符串,您可以指定輸出數據的特定格式,例如對齊、寬度和精度,語法fprintf函數的語法如下,```cintfprintf,FILEstream,constcharformat,...,```其中,`str...。
互聯網資訊 2024-09-15 11:20:39
掌握數據庫三范式,創建高效且可靠的數據庫、靈活性和安全性,雖然可能會有一些注意事項,但三范式仍然是設計健壯和可靠的關系數據庫的基礎,...。
本站公告 2024-09-13 10:29:40
作為一名PHP開發者,實現編寫干凈、可維護且高效的代碼至關重要,這不僅可以提高你的生產力,還可以幫助你構建更健壯、更可靠的應用程序,本文將介紹一些PHP最佳實踐,幫助你提高代碼質量,這些實踐涵蓋了編碼風格、錯誤處理、性能優化和安全性等各個方面,編碼風格保持一致的編碼風格對于提高代碼可讀性和可維護性至關重要,以下是一些編碼風格最佳實踐,...。
互聯網資訊 2024-09-12 14:39:38
數據存儲優化對于提高應用程序性能和降低成本至關重要,通過優化數據存儲,可以減少應用程序的響應時間、提高吞吐量并降低存儲成本,數據存儲優化包括,選擇正確的存儲類型,根據應用程序的工作負載和性能要求,選擇適當的存儲類型,例如塊存儲、文件存儲或對象存儲,優化數據結構,設計數據結構以減少冗余和提高查詢性能,例如使用索引和表分區,實現緩存,將常...。
互聯網資訊 2024-09-11 13:20:22
引言Flash,一種曾經無所不在的交互式多媒體平臺,近年來已被HTML5等更新技術所超越,Flash源碼仍然是為網站和應用程序創建引人注目的交互性和視覺效果的寶貴工具,本指南將為您提供掌握Flash源碼所需的知識和技能,從而提升您的網站和應用程序,我們將探討Flash源碼的基礎知識、各種工具,以及創建令人驚嘆的交互式體驗的最佳實踐,什...。
本站公告 2024-09-10 14:54:19
隨著云計算的蓬勃發展,越來越多的企業和個人選擇基于云的解決方案來滿足他們的計算和存儲需求,與傳統的本地軟件相比,基于云的解決方案提供了許多優勢,其中一個主要優勢就是無需本地安裝或維護,這帶來的好處包括,降低成本,無需購買和維護本地硬件,從而降低了前期成本和持續成本,提高可擴展性,云平臺可以輕松地按需縱向擴展或縮小,以適應業務需求的變化...。
互聯網資訊 2024-09-09 18:01:21
結賬是購買過程中的關鍵一步,也是許多企業面臨的挑戰,復雜且耗時的結賬流程可能會讓客戶望而卻步,導致銷售損失,為了避免這種情況,企業可以采取多種措施來簡化結賬流程,提高客戶滿意度,在本文中,我們將探討結賬后才能進行的常見操作以及這些操作如何影響客戶體驗,我們還將提供一些提示,幫助企業簡化結賬流程,結賬后才能進行的常見操作以下是一些常見的...。
互聯網資訊 2024-09-06 08:40:24
由于現在網站建設的門檻是比較低的,做網站變得非常容易,小企業甚至個人都可以利用各種自助網站建設系統來建設自己的網站,擁有自己的網站,便于宣傳、展示企業形象、推廣產品和品牌營銷,那么自己如何做網站,步驟是什么,其實很簡單,只要做以下步驟,一、下載自助建站系統目前市場上自助建站系統很多,但是并不是說所有的質量都是好的,我們需要仔細的進行辨...。
技術教程 2024-09-02 02:33:49