日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

實(shí)時(shí) Java,第 3 部分: 線程化和同步

 ShangShujie 2007-05-28
本文是關(guān)于實(shí)時(shí) Java™ 系列文章(共 5 部分)的第三篇,考察了 Java 實(shí)時(shí)規(guī)范(RTSJ)的實(shí)現(xiàn)必須支持的線程化和同步問(wèn)題。您還將了解開(kāi)發(fā)和部署實(shí)時(shí)應(yīng)用程序時(shí)必須牢記的一些有關(guān)這兩方面的基本考慮。

線程化和同步是 Java 編程語(yǔ)言的核心特性,Java 語(yǔ)言規(guī)范(JLS)中對(duì)二者作出了描述。RTSJ 用多種方式擴(kuò)展了 JLS 的核心功能。(參見(jiàn) 參考資料 中關(guān)于 JLS 和 RTSJ 的鏈接。)例如,RTSJ 引入了一些新的實(shí)時(shí)(RT)線程類(lèi)型,它們必須遵守比普通 Java 線程更加嚴(yán)格的調(diào)度策略。另一個(gè)例子是優(yōu)先級(jí)繼承,它是一種鎖定策略,定義了鎖競(jìng)爭(zhēng)時(shí)如何管理鎖同步。

理解對(duì)優(yōu)先級(jí)和優(yōu)先級(jí)序列的管理有助于理解 RTSJ 針對(duì)線程化和同步所作的更改。優(yōu)先級(jí)也是 RT 應(yīng)用程序使用的一種重要工具。本文通過(guò)討論如何管理線程優(yōu)先級(jí)和優(yōu)先級(jí)序列來(lái)描述 RTSJ 線程化和同步。討論了開(kāi)發(fā)、部署和執(zhí)行 RT 應(yīng)用程序(包括使用 IBM WebSphere® Real Time 開(kāi)發(fā)的應(yīng)用程序,參見(jiàn) 參考資料)時(shí)應(yīng)該考慮的一些方面。

理解普通的 Java 線程

JLS 中定義的線程稱(chēng)為普通 Java 線程。普通 Java 線程是 java.lang.Thread 類(lèi)的一個(gè)實(shí)例,該類(lèi)擁有從 1 到 10 的 10 個(gè)優(yōu)先級(jí)別。為了適應(yīng)大量的執(zhí)行平臺(tái),JLS 在如何實(shí)現(xiàn)、調(diào)度和管理普通 Java 線程的優(yōu)先級(jí)方面提供了很大的靈活性。

WebSphere VMs on Linux®(包括 WebSphere Real Time)使用 Linux 操作系統(tǒng)提供的本地線程化服務(wù)。您可以通過(guò)理解 Linux 的線程化和同步來(lái)學(xué)習(xí) Java 的線程化和同步。

Linux 線程化和同步

Linux 操作系統(tǒng)發(fā)展至今已經(jīng)提供了不同用戶級(jí)別的線程化實(shí)現(xiàn)。Native POSIX Thread Library(NPTL)(參見(jiàn) 參考資料)是 Linux 最新版本的戰(zhàn)略性線程化實(shí)現(xiàn),由 WebSphere VMs 所使用。NPTL 與它的前任相比優(yōu)勢(shì)在于 POSIX 兼容性和性能。在編譯時(shí)可通過(guò)系統(tǒng)的頭文件獲取 POSIX 服務(wù)??稍谶\(yùn)行時(shí)通過(guò) libpthread.so 動(dòng)態(tài)庫(kù)和底層 Linux 核心支持獲取 POSIX 服務(wù)。Linux 核心可以根據(jù)靜態(tài)控制(如線程優(yōu)先級(jí)級(jí)別)和系統(tǒng)中執(zhí)行的線程的某些動(dòng)態(tài)條件下來(lái)執(zhí)行線程調(diào)度。

POSIX 允許您創(chuàng)建具有不同線程調(diào)度策略和優(yōu)先級(jí)的 POSIX 線程(pthreads)以滿足不同應(yīng)用程序的需求。下面是三種此類(lèi)的調(diào)度策略:

  • SCHED_OTHER
  • SCHED_FIFO
  • SCHED_RR

SCHED_OTHER 策略用于傳統(tǒng)用戶任務(wù),如程序開(kāi)發(fā)工具、辦公應(yīng)用程序和 Web 瀏覽器。 SCHED_RRSCHED_FIFO 主要用于具有更高的確定性和時(shí)限需求的應(yīng)用程序。SCHED_RRSCHED_FIFO 之間的主要區(qū)別是 SCHED_RR 分時(shí)間片 執(zhí)行線程,而 SCHED_FIFO 則不是這樣。SCHED_OTHERSCHED_FIFO 策略用于 WebSphere Real Time,并在下面作出了更加詳細(xì)的描述。(我們不介紹 SCHED_RR 策略,WebSphere Real Time 沒(méi)有使用它。)

POSIX 通過(guò) pthread_mutex 數(shù)據(jù)類(lèi)型提供鎖定和同步支持。pthread_mutex 可以使用不同的鎖定策略創(chuàng)建。當(dāng)多個(gè)線程需要同時(shí)獲取同一個(gè)鎖的時(shí)候,鎖定策略常常會(huì)影響執(zhí)行行為。標(biāo)準(zhǔn)的 Linux 版本支持單個(gè)的默認(rèn)策略,而 RT Linux 版本還支持優(yōu)先級(jí)繼承鎖定策略。我們將在本文的 同步概述 一節(jié)對(duì)優(yōu)先級(jí)繼承策略作更詳細(xì)的描述。

Linux 調(diào)度和鎖定用來(lái)管理先進(jìn)先出(FIFO)隊(duì)列。

普通 Java 線程的線程調(diào)度

RTSJ 指出普通 Java 線程的行為跟 JLS 中定義的相同。在 WebSphere Real Time 中,普通 Java 線程使用 Linux 的 POSIX SCHED_OTHER 調(diào)度策略來(lái)實(shí)現(xiàn)。SCHED_OTHER 策略主要用于編譯器和字處理程序之類(lèi)的應(yīng)用程序,不能用于需要更高確定性的任務(wù)。

在 2.6 Linux 內(nèi)核中,SCHED_OTHER 策略支持 40 個(gè)優(yōu)先級(jí)級(jí)別。這 40 個(gè)優(yōu)先級(jí)級(jí)別基于處理器級(jí)別來(lái)管理,就是說(shuō):

  • 出于緩存性能的原因,Linux 嘗試在同一個(gè)處理程序中執(zhí)行線程。
  • 線程調(diào)度主要使用處理器級(jí)別的鎖而不是系統(tǒng)級(jí)別的鎖。

如有需要,Linux 可將線程從一個(gè)處理程序遷移到另一個(gè)處理程序以平衡工作量。

在(40 個(gè)中的)每個(gè)優(yōu)先級(jí)級(jí)別中,Linux 管理活動(dòng)隊(duì)列過(guò)期隊(duì)列。每個(gè)隊(duì)列包含一個(gè)線程鏈表(或者為空)。使用活動(dòng)和過(guò)期隊(duì)列出于以下目的:效率、負(fù)載平衡和其他一些目的。邏輯上可將系統(tǒng)看作:為(40 個(gè)中的)每個(gè)優(yōu)先級(jí)管理一個(gè) FIFO 序列,稱(chēng)為運(yùn)行隊(duì)列。一個(gè)從非空運(yùn)行隊(duì)列的前端分派的線程具有最高的優(yōu)先級(jí)。該線程從隊(duì)列中移除并執(zhí)行一段時(shí)間(稱(chēng)作:時(shí)間量時(shí)間片)。當(dāng)一個(gè)執(zhí)行線程超過(guò) 它的時(shí)間量時(shí),它的優(yōu)先級(jí)被放在運(yùn)行隊(duì)列的后端并給它指定了新的時(shí)間量。通過(guò)從隊(duì)列的前端分派線程和在隊(duì)列的后端放置過(guò)期的線程,程序在一個(gè)優(yōu)先級(jí)中輪替執(zhí)行。

為線程提供的時(shí)間量取決于給線程指定的優(yōu)先級(jí)。指定了較高優(yōu)先級(jí)的線程擁有較長(zhǎng)的執(zhí)行時(shí)間量。為了防止線程霸占 CPU,Linux 根據(jù)一些因素(如線程是 I/O 限制還是 CPU 限制)動(dòng)態(tài)提高或降低線程的優(yōu)先級(jí)。線程可以通過(guò)讓步(如調(diào)用 Thread.yield())自愿地放棄它的時(shí)間片,或通過(guò)阻塞放棄控制權(quán),在阻塞處等待事件發(fā)生。釋放鎖可以觸發(fā)一個(gè)這類(lèi)的事件。

WebSphere Real Time 中的 VM 沒(méi)有顯式地指定跨越 40 個(gè) SCHED_OTHER Linux 線程優(yōu)先級(jí)的 10 個(gè)普通 Java 線程優(yōu)先級(jí)。所有的普通 Java 線程,不論其 Java 優(yōu)先級(jí)如何,都被指定為默認(rèn)的 Linux 優(yōu)先級(jí)。默認(rèn)的 Linux 優(yōu)先級(jí)處于 40 個(gè) SCHED_OTHER 優(yōu)先級(jí)的中間位置。通過(guò)使用默認(rèn)優(yōu)先級(jí),普通 Java 線程可以順利地執(zhí)行,即不論 Linux 可能作出何種動(dòng)態(tài)優(yōu)先級(jí)調(diào)整,運(yùn)行隊(duì)列中的每個(gè)普通 Java 線程都能最終得到執(zhí)行。這里假設(shè)的是只執(zhí)行普通 Java 線程的系統(tǒng)而不是其他系統(tǒng),比如執(zhí)行 RT 線程的系統(tǒng)。

注意:WebSphere Real Time 中的 VM 和 WebSphere VM 的非 RT 版本都使用 SCHED_OTHER 策略并針對(duì)普通 Java 線程使用默認(rèn)優(yōu)先級(jí)指定。通過(guò)使用相同的策略,這兩種 JVM 具有相似但不相同的線程調(diào)度和同步特征。WebSphere Real Time 類(lèi)庫(kù)中的更改、JVM 中的更改和為支持 RTSJ 而在 JIT 編譯器中作出的更改,以及 RT Metronome 垃圾收集器的引入(參見(jiàn) 參考資料)使應(yīng)用程序不可能在兩種虛擬機(jī)中以相同的同步和性能特征運(yùn)行。在 IBM WebSphere Real Time 測(cè)試期間,在測(cè)試程序中,同步差異使競(jìng)爭(zhēng)條件(換言之,bug)浮出了水面,而這些測(cè)試程序已經(jīng)在其他 JVM 上運(yùn)行了很多年。

關(guān)于本文的代碼示例的一點(diǎn)注意

以下各節(jié)中的代碼示例使用帶有讓步的 spin 循環(huán)。這種方法只適用于演示的目的;不能在 RT 應(yīng)用程序中使用這種方法。

使用普通 Java 線程的代碼示例

清單 1 展示了一個(gè)使用普通 Java 線程的程序,確定了兩個(gè)線程中的每一個(gè)在五秒的時(shí)間間隔內(nèi)在一個(gè)循環(huán)中執(zhí)行的迭代次數(shù):


清單 1. 普通 Java 線程
            class myThreadClass extends java.lang.Thread {
            volatile static boolean Stop = false;
            // Primordial thread executes main()
            public static void main(String args[]) throws InterruptedException {
            // Create and start 2 threads
            myThreadClass thread1 = new myThreadClass();
            thread1.setPriority(4);    // 1st thread at 4th non-RT priority
            myThreadClass thread2 = new myThreadClass();
            thread2.setPriority(6);    // 2nd thread at 6th non-RT priority
            thread1.start();           // start 1st thread to execute run()
            thread2.start();           // start 2nd thread to execute run()
            // Sleep for 5 seconds, then tell the threads to terminate
            Thread.sleep(5*1000);
            Stop = true;
            }
            public void run() { // Created threads execute this method
            System.out.println("Created thread");
            int count = 0;
            for (;Stop != true;) {    // continue until asked to stop
            count++;
            Thread.yield();   // yield to other thread
            }
            System.out.println("Thread terminates. Loop count is " + count);
            }
            }
            

清單 1 中的程序具有三個(gè)普通 Java 線程的用戶線程:

  • 原始線程:
    • 它是啟動(dòng)過(guò)程中隱式創(chuàng)建的主線程,執(zhí)行 main() 方法。
    • main() 創(chuàng)建了兩個(gè)普通 Java 線程:一個(gè)線程的優(yōu)先級(jí)為 4 而另一個(gè)線程的優(yōu)先級(jí)為 6。
    • 主線程通過(guò)調(diào)用 Thread.sleep() 方法休眠五秒鐘來(lái)達(dá)到故意阻塞自身的目的。
    • 休眠五秒鐘后,此線程指示其他兩個(gè)線程結(jié)束。
  • 優(yōu)先級(jí)為 4 的線程:
    • 此線程由原始線程創(chuàng)建,后者執(zhí)行包含 for 循環(huán)的 run() 方法。
    • 該線程:
      1. 在每次循環(huán)迭代中增加一個(gè)計(jì)數(shù)。
      2. 通過(guò)調(diào)用 Thread.yield() 方法自愿放棄它的時(shí)間片。
      3. 在主線程發(fā)出請(qǐng)求時(shí)結(jié)束。結(jié)束前打印循環(huán)計(jì)數(shù)。
  • 優(yōu)先級(jí)為 6 的線程:此線程執(zhí)行的動(dòng)作與優(yōu)先級(jí)為 4 的線程相同。

如果此程序在單處理器或卸載的多處理器上運(yùn)行,則每個(gè)線程打印的 for 循環(huán)迭代計(jì)數(shù)大致相同。在一次運(yùn)行中,程序?qū)⒋蛴。?

Created thread
            Created thread
            Thread terminates. Loop count is 540084
            Thread terminates. Loop count is 540083
            

如果刪除對(duì) Thread.yield() 的調(diào)用,則兩個(gè)線程的循環(huán)計(jì)數(shù)可能相近,但絕不可能相同。在 SCHED_OTHER 策略中為這兩個(gè)線程都指定了相同的默認(rèn)優(yōu)先級(jí)。因此給兩個(gè)線程分配了相同的時(shí)間片執(zhí)行。因?yàn)榫€程執(zhí)行的是相同的代碼,所以它們應(yīng)作出類(lèi)似的動(dòng)態(tài)優(yōu)先級(jí)調(diào)整 并在相同的運(yùn)行隊(duì)列中輪替執(zhí)行。但是由于首先執(zhí)行優(yōu)先級(jí)為 4 的線程,因此在五秒鐘的執(zhí)行時(shí)間間隔中,它分得的時(shí)間稍多一些并且打印的循環(huán)計(jì)數(shù)也稍大一些。





回頁(yè)首


理解 RT 線程

RT 線程是 javax.realtime.RealtimeThread 的一個(gè)實(shí)例。RTSJ 要求規(guī)范的實(shí)現(xiàn)必須為 RT 線程提供至少 28 個(gè)連續(xù)的優(yōu)先級(jí)。這些優(yōu)先級(jí)被稱(chēng)作實(shí)時(shí)優(yōu)先級(jí)。規(guī)范中并沒(méi)有指定 RT 優(yōu)先級(jí)范圍的開(kāi)始值,除非其優(yōu)先級(jí)高于 10 —— 普通 Java 線程的最高優(yōu)先級(jí)值。出于可移植性的原因,應(yīng)用程序代碼應(yīng)使用新的 PriorityScheduler 類(lèi)的 getPriorityMin()getPriorityMax() 方法來(lái)確定可用的 RT 優(yōu)先級(jí)值的范圍。

對(duì) RT 線程的推動(dòng)

JLS 中的線程調(diào)度并不精確而且只提供了 10 個(gè)優(yōu)先級(jí)值。由 Linux 實(shí)現(xiàn)的 POSIX SCHED_OTHER 策略滿足了各種應(yīng)用程序的需要。但是 SCHED_OTHER 策略具有一些不好的特性。動(dòng)態(tài)優(yōu)先級(jí)調(diào)整和時(shí)間片劃分可能在不可預(yù)測(cè)的時(shí)間內(nèi)發(fā)生。SCHED_OTHER 優(yōu)先級(jí)的值(40)其實(shí)并不算大,其中一部分已經(jīng)被使用普通 Java 線程的應(yīng)用程序和動(dòng)態(tài)優(yōu)先級(jí)調(diào)整利用了。JVM 還需要對(duì)內(nèi)部線程使用優(yōu)先級(jí)以達(dá)到一些特殊目的,比如垃圾收集(GC)。

缺少確定性、需要更高的優(yōu)先級(jí)級(jí)別以及要求與現(xiàn)有應(yīng)用程序兼容,這些因素引發(fā)了對(duì)擴(kuò)展的需求,這將為 Java 程序員提供新的調(diào)度功能。RTSJ 中描述的 javax.realtime 包中的類(lèi)提供了這些功能。在 WebSphere Real Time 中,Linux SCHED_FIFO 調(diào)度策略滿足了 RTSJ 調(diào)度需求。

RT Java 線程的線程調(diào)度

在 WebSphere Real Time 中,支持 28 個(gè) RT Java 優(yōu)先級(jí),其范圍為 11 到 38。PriorityScheduler 類(lèi)的 API 應(yīng)用于檢索這個(gè)范圍。本節(jié)描述了比 RTSJ 更多的線程調(diào)度細(xì)節(jié)以及 Linux SCHED_FIFO 策略的一些方面,已經(jīng)超出了 RTSJ 的需求。

RTSJ 將 RT 優(yōu)先級(jí)視作由運(yùn)行時(shí)系統(tǒng)在邏輯上實(shí)現(xiàn)的優(yōu)先級(jí),該系統(tǒng)為每個(gè) RT 優(yōu)先級(jí)保持一個(gè)獨(dú)立隊(duì)列。線程調(diào)度程序必須從非空的最高優(yōu)先級(jí)隊(duì)列的頭部開(kāi)始調(diào)度。注意:如果所有隊(duì)列中的線程都不具有 RT 優(yōu)先級(jí),則調(diào)度一個(gè)普通 Java 線程按 JLS 中的描述執(zhí)行(參見(jiàn) 普通 Java 線程的線程調(diào)度)。

具有 RT 優(yōu)先級(jí)的調(diào)度線程可以一直執(zhí)行直至阻塞,通過(guò)讓步自愿放棄控制權(quán),或被具有更高 RT 優(yōu)先級(jí)的線程搶占。具有 RT 優(yōu)先級(jí)并自愿讓步的線程的優(yōu)先級(jí)被置于隊(duì)列的后端。RTSJ 還要求此類(lèi)調(diào)度在不變的時(shí)間內(nèi)進(jìn)行,并且不能隨某些因素變化(如當(dāng)前執(zhí)行的 RT 線程的數(shù)量)。RTSJ 的 1.02 版本對(duì)單處理器系統(tǒng)應(yīng)用了這些規(guī)則;RTSJ 對(duì)于多處理器系統(tǒng)上的調(diào)度如何運(yùn)作未作要求。

Linux 為所有適當(dāng)?shù)?RTSJ 調(diào)度需求提供了 SCHED_FIFO 策略。SCHED_FIFO 策略用于 RT 而不用于用戶任務(wù)。SCHED_FIFOSCHED_OTHER 策略的區(qū)別在于前者提供了 99 個(gè)優(yōu)先級(jí)級(jí)別。SCHED_FIFO 不為線程分時(shí)間片。同樣,SCHED_FIFO 策略也不動(dòng)態(tài)調(diào)整 RT 線程的優(yōu)先級(jí),除非通過(guò)優(yōu)先級(jí)繼承鎖定策略(同步概述 一節(jié)對(duì)此作出了描述)。由于優(yōu)先級(jí)繼承的原因,RTSJ 需要使用優(yōu)先級(jí)調(diào)整。

Linux 為 RT 線程和普通 Java 線程提供不變時(shí)間調(diào)度。在多處理器系統(tǒng)中,Linux 試圖模擬分派到可用處理器的單個(gè)全局 RT 線程隊(duì)列的行為。這與 RTSJ 的精神最為接近,但確實(shí)與用于普通 Java 線程的 SCHED_OTHER 策略不同。

使用 RT 線程的有問(wèn)題的代碼示例

清單 2 修改 清單 1 中的代碼來(lái)創(chuàng)建 RT 線程而不是普通 Java 線程。使用 java.realtime.RealtimeThread 而不是 java.lang.Thread 指出了其中的區(qū)別。第一個(gè)線程創(chuàng)建于第 4 RT 優(yōu)先級(jí)而第二個(gè)線程創(chuàng)建于第 6 RT 優(yōu)先級(jí),與 getPriorityMin() 方法確定的相同。


清單 2. RT 線程
            import javax.realtime.*;
            class myRealtimeThreadClass extends javax.realtime.RealtimeThread {
            volatile static boolean Stop = false;
            // Primordial thread executes main()
            public static void main(String args[]) throws InterruptedException {
            // Create and start 2 threads
            myRealtimeThreadClass thread1 = new myRealtimeThreadClass();
            // want 1st thread at 4th real-time priority
            thread1.setPriority(PriorityScheduler.getMinPriority(null)+ 4);
            myRealtimeThreadClass thread2 = new myRealtimeThreadClass();
            // want 2nd thread at 6th real-time priority
            thread2.setPriority(PriorityScheduler.getMinPriority(null)+ 6);
            thread1.start();           // start 1st thread to execute run()
            thread2.start();           // start 2nd thread to execute run()
            // Sleep for 5 seconds, then tell the threads to terminate
            Thread.sleep(5*1000);
            Stop = true;
            }
            public void run() { // Created threads execute this method
            System.out.println("Created thread");
            int count = 0;
            for (;Stop != true;) {    // continue until asked to stop
            count++;
            // Thread.yield();   // yield to other thread
            }
            System.out.println("Thread terminates. Loop count is " + count);
            }
            }
            

清單 2 中修改后的代碼存在一些問(wèn)題。 如果程序在單處理器環(huán)境中運(yùn)行,則它永遠(yuǎn)不會(huì)結(jié)束并且只能打印以下內(nèi)容:

Created thread
            

出現(xiàn)這樣的結(jié)果可以用 RT 線程調(diào)度的行為來(lái)解釋。原始線程仍然是一個(gè)普通 Java 線程并利用非 RT(SCHED_OTHER)策略運(yùn)行。只要原始線程啟動(dòng)第一個(gè) RT 線程,RT 線程就搶占原始線程并且 RT 線程將會(huì)不確定地運(yùn)行,因?yàn)樗皇軙r(shí)間量和線程阻塞的限制。原始線程被搶占后,就再也不允許執(zhí)行,因此再也不會(huì)啟動(dòng)第二個(gè) RT 線程。Thread.yield() 對(duì)允許原始線程執(zhí)行反而不起作用 —— 因?yàn)樽尣竭壿媽?RT 線程置于其運(yùn)行隊(duì)列的末端 —— 但是線程調(diào)度程序?qū)⒃俅握{(diào)度這個(gè)線程,因?yàn)樗沁\(yùn)行隊(duì)列前端的具有最高優(yōu)先級(jí)的線程。

該程序在雙處理器系統(tǒng)中同樣會(huì)失敗。它將打印以下內(nèi)容:

Created thread
            Created thread
            

允許使用原始線程創(chuàng)建這兩個(gè) RT 線程。但是創(chuàng)建第二個(gè)線程后,原始線程被搶占并且再也不允許告知線程結(jié)束,因?yàn)閮蓚€(gè) RT 線程在兩個(gè)處理器上執(zhí)行而且永遠(yuǎn)不會(huì)阻塞。

在帶有三個(gè)或更多處理器的系統(tǒng)上,程序運(yùn)行至完成并生成一個(gè)結(jié)果。

單處理器上運(yùn)行的 RT 代碼示例

清單 3 顯示了修改后能在單處理器系統(tǒng)中正確運(yùn)行的代碼。main() 方法的邏輯被移到了一個(gè)具有第 8 RT 優(yōu)先級(jí)的 “main” RT 線程中。這個(gè)優(yōu)先級(jí)比主 RT 線程創(chuàng)建的兩個(gè)其他 RT 線程的優(yōu)先級(jí)都要高。擁有最高的 RT 優(yōu)先級(jí)使這個(gè)主 RT 線程能夠成功地創(chuàng)建兩個(gè) RT 線程,并且還允許它從五秒鐘的休眠中蘇醒時(shí)能夠搶占當(dāng)前運(yùn)行的線程。


清單 3. 修改后的 RT 線程示例
            import javax.realtime.*;
            class myRealtimeThreadClass extends javax.realtime.RealtimeThread {
            volatile static boolean Stop = false;
            static class myRealtimeStartup extends javax.realtime.RealtimeThread {
            public void run() {
            // Create and start 2 threads
            myRealtimeThreadClass thread1 = new myRealtimeThreadClass();
            // want 1st thread at 4th real-time priority
            thread1.setPriority(PriorityScheduler.getMinPriority(null)+ 4);
            myRealtimeThreadClass thread2 = new myRealtimeThreadClass();
            // want 1st thread at 6th real-time priority
            thread2.setPriority(PriorityScheduler.getMinPriority(null)+ 6);
            thread1.start();           // start 1st thread to execute run()
            thread2.start();           // start 2nd thread to execute run()
            // Sleep for 5 seconds, then tell the threads to terminate
            try {
            Thread.sleep(5*1000);
            } catch (InterruptedException e) {
            }
            myRealtimeThreadClass.Stop = true;
            }
            }
            // Primordial thread creates real-time startup thread
            public static void main(String args[]) {
            myRealtimeStartup startThr = new myRealtimeStartup();
            startThr.setPriority(PriorityScheduler.getMinPriority(null)+ 8);
            startThr.start();
            }
            public void run() { // Created threads execute this method
            System.out.println("Created thread");
            int count = 0;
            for (;Stop != true;) {    // continue until asked to stop
            count++;
            // Thread.yield();   // yield to other thread
            }
            System.out.println("Thread terminates. Loop count is " + count);
            }
            }
            

當(dāng)此程序在單處理器上運(yùn)行時(shí),它將打印以下結(jié)果:

Created thread
            Thread terminates. Loop count is 32767955
            Created thread
            Thread terminates. Loop count is 0
            

程序的輸出顯示所有的線程運(yùn)行并結(jié)束,但是這兩個(gè)線程只有一個(gè)執(zhí)行 for 循環(huán)的一個(gè)迭代。這個(gè)輸出可通過(guò)考慮 RT 線程的優(yōu)先級(jí)來(lái)解釋。主 RT 線程一直運(yùn)行,直至調(diào)用 Thread.sleep() 方法來(lái)阻塞線程。主 RT 線程創(chuàng)建了兩個(gè) RT 線程,但是只有第二個(gè) RT 線程(具有第 6 RT 優(yōu)先級(jí))才能夠在主 RT 線程休眠時(shí)運(yùn)行。這個(gè)線程一直運(yùn)行,直至主 RT 線程從休眠中蘇醒并指示線程結(jié)束。主 RT 線程一旦結(jié)束,就允許執(zhí)行具有第 6 優(yōu)先級(jí)的線程并結(jié)束。程序按這種方式執(zhí)行并打印具有非零值循環(huán)計(jì)數(shù)。此線程結(jié)束后,就允許運(yùn)行具有第 4 RT 優(yōu)先級(jí)的線程,但它只是繞過(guò) for 循環(huán),因?yàn)橄到y(tǒng)指示結(jié)束該線程。該線程將打印零循環(huán)計(jì)數(shù)值然后結(jié)束。





回頁(yè)首


RT 應(yīng)用程序的線程化考慮

移植應(yīng)用程序以使用 RT 線程或編寫(xiě)新應(yīng)用程序以利用 RT 線程化時(shí)需要考慮 RT 線程化的一些特性,本節(jié)將討論這些特性。

RT 線程的新擴(kuò)展

RTSJ 指定了一些工具,用于創(chuàng)建在某個(gè)特定或相關(guān)時(shí)間啟動(dòng)的 RT 線程。您可以創(chuàng)建一個(gè)線程,用于在指定的時(shí)間間隔或時(shí)期內(nèi)運(yùn)行某種邏輯。您可以定義一個(gè)線程,用于未在指定時(shí)期內(nèi)完成此邏輯時(shí)執(zhí)行(激發(fā))一個(gè) AsynchronousEventHandler(AEH)。您還可以定義線程所能夠使用的內(nèi)存類(lèi)型和數(shù)量的限制,如果超過(guò)該限制,則拋出 OutOfMemoryError。這些工具只對(duì) RT 線程可用,而對(duì)普通 Java 線程不可用。您可以在 RTSJ 中找到關(guān)于這些工具的更多信息。

Thread.interrupt() 和 pending 異常

RT 線程擴(kuò)展了 Thread.interrupt() 行為。此 API 會(huì)像 JLS 中描述的那樣中斷被阻塞的進(jìn)程。如果用戶在方法聲明中加入 Throws AsynchronouslyInterruptedException 子句,顯式地將其標(biāo)記為可中斷,也會(huì)引起這個(gè)異常。該異常也會(huì)困擾 線程,用戶必須顯式地清除異常;否則它會(huì)一直困擾(術(shù)語(yǔ)為 pending)線程。如果用戶不清除異常,則線程會(huì)伴隨著該異常而結(jié)束。如果線程以 “常規(guī)” 形式結(jié)束,但是不是在按自身形式進(jìn)行 RT 線程入池的應(yīng)用程序中,這種錯(cuò)誤危害不大,就是說(shuō),線程返回池中時(shí)仍然隨附 InterruptedException。在這種情況下,執(zhí)行線程入池的代碼應(yīng)顯式地清除異常;否則,當(dāng)重新分配具有隨附異常的入池線程時(shí),可能欺騙性地拋出異常。

原始線程和應(yīng)用程序調(diào)度邏輯

原始線程通常都是普通 Java 線程 —— 而不是 RT 線程。第一個(gè) RT 線程總是由普通 Java 線程創(chuàng)建。如果沒(méi)有足夠的可用處理器來(lái)同時(shí)運(yùn)行 RT 線程和普通 Java 線程,則這個(gè) RT 線程會(huì)立即搶占普通 Java 線程。搶占可以防止普通 Java 線程繼續(xù)創(chuàng)建 RT 線程或其他邏輯,以便將應(yīng)用程序置于適當(dāng)?shù)某跏蓟癄顟B(tài)。

您 可以通過(guò)從一個(gè)高優(yōu)先級(jí) RT 線程執(zhí)行應(yīng)用程序初始化來(lái)避免這個(gè)問(wèn)題。執(zhí)行自身形式的線程入池和線程調(diào)度的應(yīng)用程序或庫(kù)可能需要這種技術(shù)。即,線程調(diào)度邏輯應(yīng)該以高優(yōu)先級(jí)運(yùn)行,或在高 優(yōu)先級(jí)的線程中運(yùn)行。為執(zhí)行線程入池邏輯選擇適當(dāng)?shù)膬?yōu)先級(jí)有助于防止線程入隊(duì)和出隊(duì)中遇到的問(wèn)題。

失控線程

普 通 Java 線程按時(shí)間量執(zhí)行,而動(dòng)態(tài)優(yōu)先級(jí)根據(jù) CPU 的使用調(diào)整調(diào)度程序的執(zhí)行,允許所有的普通 Java 線程最后執(zhí)行。反過(guò)來(lái),RT 線程不受時(shí)間量的限制,并且線程調(diào)度程序不根據(jù) CPU 的使用進(jìn)行任何形式的動(dòng)態(tài)優(yōu)先級(jí)調(diào)整。普通 Java 線程和 RT 線程之間的調(diào)度策略差異使失控 RT 線程的出現(xiàn)成為可能。失控 RT 線程可以控制系統(tǒng)并阻止所有其他應(yīng)用程序的運(yùn)行,阻止用戶登錄系統(tǒng)等等。

在開(kāi)發(fā)和測(cè)試期間,有一種技術(shù)可以幫助減輕失控線程的影響,即限制進(jìn)程能夠使用的 CPU 數(shù)量。在 Linux 上,限制 CPU 的使用使進(jìn)程在耗盡 CPU 限制時(shí)終止失控線程。另外,監(jiān)控系統(tǒng)狀態(tài)或提供系統(tǒng)登錄的程序應(yīng)該以高 RT 優(yōu)先級(jí)運(yùn)行,以便程序可以搶占問(wèn)題線程。

從 Java 優(yōu)先級(jí)到操作系統(tǒng)優(yōu)先級(jí)的映射

在 Linux 上,POSIX SCHED_FIFO 策略提供了從 1 到 99 的整數(shù)范圍內(nèi)的 99 個(gè) RT 優(yōu)先級(jí)。在這個(gè)系統(tǒng)范圍內(nèi),從 11 到 89 的優(yōu)先級(jí)由 WebSphere VM 使用,此范圍的一個(gè)子集用來(lái)實(shí)現(xiàn) 28 個(gè) RTSJ 優(yōu)先級(jí)。28 個(gè) RT Java 優(yōu)先級(jí)映射到此范圍的 POSIX 系統(tǒng)優(yōu)先級(jí),IBM WebSphere Real Time 文檔中對(duì)這一點(diǎn)作出了描述。但是應(yīng)用程序代碼不應(yīng)該依賴這個(gè)映射,而只應(yīng)該依賴于 Java 級(jí)別的 28 個(gè) RT 優(yōu)先級(jí)的相關(guān)順序。這樣 JVM 可以在未來(lái)的 WebSphere Real Time 版本中重新映射這個(gè)范圍并提供改進(jìn)。

如果某些端口監(jiān)督程序或 RT 進(jìn)程需要的優(yōu)先級(jí)高于或低于 WebSphere Real Time 中使用的優(yōu)先級(jí),則應(yīng)用程序可以使用 SCHED_FIFO 優(yōu)先級(jí) 1 或優(yōu)先級(jí) 90 來(lái)實(shí)現(xiàn)這些程序或進(jìn)程。

JNI AttachThread()

Java Native Interface (JNI) 允許使用 JNI AttachThread() API 將使用 C 代碼創(chuàng)建的線程加入到 JVM 中,但 RTSJ 并不對(duì) JNI 接口進(jìn)行更改或配置以便加入 RT 線程。因此,應(yīng)用程序應(yīng)避免用 C 代碼創(chuàng)建準(zhǔn)備加入到 JVM 中的 POSIX RT 線程。反過(guò)來(lái),應(yīng)該在 Java 語(yǔ)言中創(chuàng)建此類(lèi) RT 線程。

派生進(jìn)程和 RT 優(yōu)先級(jí)

一 個(gè)線程可以派生另一個(gè)進(jìn)程。在 Linux 上,派生進(jìn)程的原始線程繼承派生它的父線程的優(yōu)先級(jí)。如果派生進(jìn)程是一個(gè) JVM,則 JVM 的原始線程創(chuàng)建時(shí)具有 RT 優(yōu)先級(jí)。這將與普通 Java 線程的順序沖突,比如原始線程的調(diào)度優(yōu)先級(jí)比 RT 線程低。為了防止這種情形,JVM 強(qiáng)制原始線程擁有非 RT 優(yōu)先級(jí) —— 即擁有 SCHED_OTHER 策略。

Thread.yield()

Thread.yield() 只讓步給具有相同優(yōu)先級(jí)的線程,決不會(huì)讓步給高于或低于自身優(yōu)先級(jí)的線程。只讓步給具有相同優(yōu)先級(jí)的線程意味著在使用多個(gè) RT 優(yōu)先級(jí)的 RT 應(yīng)用程序中使用 Thread.yield() 可能會(huì)出現(xiàn)問(wèn)題。應(yīng)該避免使用 Thread.yield(),除非完全有必要。

NoHeapRealtimeThreads

javax.realtime.NoHeapRealtimeThread (NHRT) 是 RTSJ 中的另一種新的線程類(lèi)型,它是 javax.realtime.RealtimeThread 的一個(gè)子類(lèi)。NHRT 具有與我們所描述的 RT 線程相同的調(diào)度特征,只是 NHRT 不會(huì)被 GC 搶占并且 NHRT 無(wú)法讀或?qū)?Java 堆。NHRT 是 RTSJ 的一個(gè)重要方面,本系列的后續(xù)文章中將對(duì)它進(jìn)行討論。

AsynchronousEventHandlers

AsynchronousEventHandler (AEH) 是 RTSJ 附帶的新增程序,可將它視為發(fā)生事件時(shí)執(zhí)行的一種 RT 線程。例如,可以設(shè)置 AEH 在某個(gè)特定或關(guān)聯(lián)時(shí)間激發(fā)。AEH 還具有與 RT 線程相同的調(diào)度特征并具有堆和非堆兩種風(fēng)格。





回頁(yè)首


同步概述

許多 Java 應(yīng)用程序直接使用 Java 線程化特性,或正在開(kāi)發(fā)中的應(yīng)用程序使用涉及多個(gè)線程的庫(kù)。多線程編程中的一個(gè)主要考慮是確保程序在執(zhí)行多線程的系統(tǒng)中正確地 —— 線程安全地 —— 運(yùn)行。要保證程序線程安全地運(yùn)行,需要序列化訪問(wèn)由多個(gè)使用同步原語(yǔ)(如鎖或原子機(jī)器操作)的線程共享的數(shù)據(jù)。RT 應(yīng)用程序的編程人員通常面臨使程序按某種時(shí)間約束執(zhí)行的挑戰(zhàn)。為了應(yīng)對(duì)這個(gè)挑戰(zhàn),他們可能需要了解當(dāng)前使用組件的實(shí)現(xiàn)細(xì)節(jié)、含意和性能屬性。

本文的剩余部分將討論 Java 語(yǔ)言提供的核心同步原語(yǔ)的各個(gè)方面,這些原語(yǔ)在 RTSJ 中如何更改,以及 RT 編程人員使用這些原語(yǔ)時(shí)需要注意的一些暗示。

Java 語(yǔ)言同步概述

Java 語(yǔ)言提供了三種核心同步原語(yǔ):

  • 同步的方法和代碼塊允許線程在入口處鎖定對(duì)象并在出口處解鎖(針對(duì)方法或代碼塊)。
  • Object.wait() 釋放對(duì)象鎖,線程等待。
  • Object.notify()wait() 對(duì)象的線程解鎖。notifyAll() 為所有等待的線程解鎖。

執(zhí)行 wait()notify() 的線程當(dāng)前必須已經(jīng)鎖定對(duì)象。

當(dāng)線程試圖鎖定的對(duì)象已被其他線程鎖定時(shí)將發(fā)生鎖爭(zhēng)用。當(dāng)發(fā)生這種情況時(shí),沒(méi)有獲得鎖的線程被置于對(duì)象的鎖爭(zhēng)用者的一個(gè)邏輯隊(duì)列中。類(lèi)似地,幾個(gè)線程可能對(duì)同一個(gè)對(duì)象執(zhí)行 Object.wait(),因此該對(duì)象擁有一個(gè)等待者的邏輯隊(duì)列。JLS 沒(méi)有指定如何管理這些隊(duì)列,但是 RTSJ 規(guī)定了這個(gè)行為。

基于優(yōu)先級(jí)的同步隊(duì)列

RTSJ 的原理是所有的線程隊(duì)列都是 FIFO 并且是基于優(yōu)先級(jí)的?;趦?yōu)先級(jí)的 FIFO 行為 —— 在前面的同步示例中,將接著執(zhí)行具有最高優(yōu)先級(jí)的線程 —— 也適用于鎖爭(zhēng)用者和鎖等待者的隊(duì)列。從邏輯觀點(diǎn)來(lái)看,鎖爭(zhēng)用者的 FIFO 基于優(yōu)先級(jí)的隊(duì)列與等待執(zhí)行的線程執(zhí)行隊(duì)列相似。同樣有相似的鎖等待者隊(duì)列。

釋放鎖以后,系統(tǒng)從爭(zhēng)用者的最高優(yōu)先級(jí)隊(duì)列的前端選擇線程,以便試圖鎖定對(duì)象。類(lèi)似地,完成 notify() 以后,等待者的最高優(yōu)先級(jí)隊(duì)列前端的線程從等待中解除阻塞。鎖釋放或鎖 notify() 操作與調(diào)度分派操作類(lèi)似,因?yàn)槎际菍?duì)最高優(yōu)先級(jí)隊(duì)列頭部的線程起作用。

為了支持基于優(yōu)先級(jí)的同步,需要對(duì) RT Linux 作一些修改。還需要對(duì) WebSphere Real Time 中的 VM 作出更改,以便在執(zhí)行 notify() 操作時(shí)委托 Linux 選擇對(duì)哪一個(gè)線程解除阻塞。

優(yōu)先級(jí)反轉(zhuǎn)和優(yōu)先級(jí)繼承

優(yōu)先級(jí)反轉(zhuǎn) 指的是阻塞高優(yōu)先級(jí)線程的鎖由低優(yōu)先級(jí)線程持有。中等優(yōu)先級(jí)線程可能搶占低優(yōu)先級(jí)線程,同時(shí)持有鎖并優(yōu)先于低優(yōu)先級(jí)線程運(yùn)行。優(yōu)先級(jí)反轉(zhuǎn)將延遲低優(yōu)先級(jí)線程和高優(yōu)先級(jí)線程的執(zhí)行。優(yōu)先級(jí)反轉(zhuǎn)導(dǎo)致的延遲可能導(dǎo)致無(wú)法滿足關(guān)鍵的時(shí)限。圖 1 的第一條時(shí)間線顯示這種情況。

優(yōu)先級(jí)繼承 是一種用于避免優(yōu)先級(jí)反轉(zhuǎn)的技術(shù)。優(yōu)先級(jí)繼承由 RTSJ 規(guī)定。優(yōu)先級(jí)繼承背后的思想是鎖爭(zhēng)用,鎖持有者的優(yōu)先級(jí)被提高到希望獲取鎖的線程的優(yōu)先級(jí)。當(dāng)鎖持有者釋放鎖時(shí),它的優(yōu)先級(jí)則被 “降” 回基本優(yōu)先級(jí)。在剛剛描述的場(chǎng)景中,發(fā)生鎖爭(zhēng)用時(shí)低優(yōu)先級(jí)的線程以高優(yōu)先級(jí)運(yùn)行,直至線程釋放鎖。鎖釋放后,高優(yōu)先級(jí)線程鎖定對(duì)象并繼續(xù)執(zhí)行。中等優(yōu)先級(jí) 線程禁止延遲高優(yōu)先級(jí)線程。圖 1 中的第二條時(shí)間線顯示了發(fā)生優(yōu)先級(jí)繼承時(shí)第一條時(shí)間線的鎖定行為的變化情況。


圖 1. 優(yōu)先級(jí)反轉(zhuǎn)和優(yōu)先級(jí)繼承
優(yōu)先級(jí)反轉(zhuǎn)和優(yōu)先級(jí)繼承

可能存在下面一種情況:高優(yōu)先級(jí)線程試圖獲取低優(yōu)先級(jí)線程持有的鎖,而低優(yōu)先級(jí)線程自身又被另一個(gè)線程持有的另一個(gè)鎖阻塞。在這種情況下,低優(yōu)先級(jí)線程和另一個(gè)線程都會(huì)被提高優(yōu)先級(jí)。就是說(shuō),優(yōu)先級(jí)繼承需要對(duì)一組線程進(jìn)行優(yōu)先級(jí)提高和降低。

優(yōu)先級(jí)繼承實(shí)現(xiàn)

優(yōu)先級(jí)繼承是通過(guò) Linux 內(nèi)核功能來(lái)提供的,通過(guò) POSIX 鎖定服務(wù)可將后者導(dǎo)出到用戶空間。完全位于用戶空間中的解決方案并不令人滿意,因?yàn)椋?/p>

  • Linux 內(nèi)核可能被搶占并且常常出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)。對(duì)于某些系統(tǒng)鎖也需要使用優(yōu)先級(jí)繼承。
  • 嘗試用戶空間中的解決方案導(dǎo)致難于解決的競(jìng)態(tài)條件。
  • 優(yōu)先級(jí)提高總是需要使用內(nèi)核調(diào)用。

POSIX 鎖的類(lèi)型為 pthread_mutex。用于創(chuàng)建 pthread_mutex 的 POSIX API 使用互斥鎖來(lái)實(shí)現(xiàn)優(yōu)先級(jí)繼承協(xié)議。有一些 POSIX 服務(wù)可用于鎖定 pthread_mutex 和為 pthread_mutex 解鎖。在這些情況下優(yōu)先級(jí)繼承支持生效。Linux 在沒(méi)有鎖爭(zhēng)用的情況下執(zhí)行用戶空間中的所有鎖定。當(dāng)發(fā)生鎖爭(zhēng)用時(shí),在內(nèi)核空間中進(jìn)行優(yōu)先級(jí)提高和同步隊(duì)列管理。

WebSphere VM 使用 POSIX 鎖定 API 來(lái)實(shí)現(xiàn)我們先前所描述的用于支持優(yōu)先級(jí)繼承的核心 Java 語(yǔ)言同步原語(yǔ)。用戶級(jí)別的 C 代碼也可以使用這些 POSIX 服務(wù)。對(duì)于 Java 級(jí)別的鎖定操作,分配了一個(gè)惟一的 pthread_mutex 并使用原子機(jī)器操作將其綁定到 Java 對(duì)象。對(duì)于 Java 級(jí)別的解鎖操作,使用原子操作解除 pthread_mutex 與對(duì)象之間的綁定,前提是不存在鎖爭(zhēng)用。存在鎖爭(zhēng)用時(shí),POSIX 鎖定和解鎖操作將觸發(fā) Linux 內(nèi)核優(yōu)先級(jí)繼承支持。

為了幫助實(shí)現(xiàn)互斥鎖分配和鎖定時(shí)間的最小化,JVM 管理一個(gè)全局鎖緩存和一個(gè)單線程鎖緩存,其中每個(gè)緩存包含了未分配的 pthread_mutex。線程專(zhuān)用緩存中的互斥鎖從全局鎖緩存中獲得?;コ怄i在放入線程鎖定緩存之前被線程預(yù)先鎖定。非爭(zhēng)用的解鎖操作將一個(gè)鎖定的互斥鎖返回給線程鎖定緩存。此處假定以非爭(zhēng)用的鎖定為標(biāo)準(zhǔn),而 POSIX 級(jí)別的鎖定則通過(guò)重用預(yù)先鎖定的互斥鎖來(lái)得到減少和攤銷(xiāo)。

JVM 自身?yè)碛袃?nèi)部鎖,用于序列化對(duì)關(guān)鍵 JVM 資源(如線程列表和全局鎖緩存)的訪問(wèn)。這些鎖基于優(yōu)先級(jí)繼承并且其持有時(shí)間較短。





回頁(yè)首


RT 應(yīng)用程序的同步考慮

本節(jié)將介紹 RT 同步的一些特性,這些特性可以幫助移植應(yīng)用程序的開(kāi)發(fā)人員使用 RT 線程或編寫(xiě)新的應(yīng)用程序以使用 RT 線程化。

普通 Java 線程和 RT 線程之間的鎖爭(zhēng)用

RT 線程可能被普通 Java 線程持有的鎖阻塞。發(fā)生這種情況時(shí),優(yōu)先級(jí)繼承接管線程,因此普通 Java 線程的優(yōu)先級(jí)被提高到 RT 線程的優(yōu)先級(jí),并且只要它持有鎖就一直保持該優(yōu)先級(jí)。普通 Java 線程繼承了 RT 線程的所有調(diào)度特征:

  • 普通 Java 線程按 SCHED_FIFO 策略運(yùn)行,因此線程不劃分時(shí)間片。
  • 在提高了優(yōu)先級(jí)的 RT 運(yùn)行隊(duì)列中進(jìn)行調(diào)度和讓步。

此行為在普通 Java 線程釋放鎖時(shí)返回到 SCHED_OTHER。如果 清單 1 中創(chuàng)建的兩個(gè)線程在持有 RT 線程所需要的鎖的時(shí)候都不運(yùn)行,則該程序?qū)⒉粫?huì)結(jié)束并且出現(xiàn)我們?cè)?使用 RT 線程的問(wèn)題代碼示例 部分中描述的問(wèn)題。因?yàn)榭赡艹霈F(xiàn)這種情形,所以對(duì)于所有在實(shí)時(shí) JVM 中執(zhí)行的線程來(lái)說(shuō),執(zhí)行 spin 循環(huán)和讓步并不明智。

NHRT 和 RT 線程之間的鎖爭(zhēng)用

NHRT 可能在 RT 線程(或相應(yīng)的普通 Java 線程)持有的鎖處阻塞。雖然 RT 線程持有鎖,但是 GC 可能搶占 RT 并間接地?fù)屨? NHRT。NHRT 需要一直等到 RT 不再被 GC 搶占并釋放鎖后才能執(zhí)行。如果 NHRT 執(zhí)行的功能具有嚴(yán)格的時(shí)間要求,則 GC 搶占 NHRT 將是一個(gè)嚴(yán)重的問(wèn)題。

WebSphere Real Time 中具有確定性的垃圾收集器將暫停時(shí)間保持在一毫秒以下,使 NHRT 搶占更具有確定性。如果不能容忍此類(lèi)暫停,則可以通過(guò)避免 NHRT 和 RT 線程之間的鎖共享來(lái)繞過(guò)該問(wèn)題。如果強(qiáng)制使用鎖定,則可以考慮使用特定于 RT 或 NHRT 的資源和鎖。例如,實(shí)現(xiàn)線程入池的應(yīng)用程序可以考慮對(duì) NHRT 和 RT 線程使用分開(kāi)的池和池鎖。

同樣,javax.realtime 包提供了以下的類(lèi):

  • WaitFreeReadQueue 類(lèi)主要用于將對(duì)象從 RT 線程傳遞到 NHRT。
  • WaitFreeWriteQueue 類(lèi)主要用于將對(duì)象從 NHRT 傳遞到 RT 線程。

RT 線程在執(zhí)行無(wú)等待操作時(shí)可能被 GC 阻塞,上述類(lèi)保證了 RT 線程在執(zhí)行無(wú)等待操作時(shí)不會(huì)持有 NHRT 所需的鎖。

javax.realtime 包中的同步

某些 javax.realtime 方法故意沒(méi)有實(shí)現(xiàn)同步,因?yàn)榧词规i是無(wú)爭(zhēng)用的,同步也會(huì)造成系統(tǒng)開(kāi)銷(xiāo)。如果需要同步,則調(diào)用方負(fù)責(zé)封裝同步方法或塊中所需的 javax.realtime 方法。編程人員在使用 java.realtime 包的方法時(shí)必須考慮添加此類(lèi)同步。

核心 JLS 包中的同步

相反,如 java.util.Vector 之類(lèi)的核心 JLS 服務(wù)已經(jīng)實(shí)現(xiàn)同步。同樣,某些核心 JLS 服務(wù)可以執(zhí)行一些內(nèi)部鎖定來(lái)序列化某些共享資源。由于這種同步,在使用核心 JLS 服務(wù)時(shí),必須謹(jǐn)慎執(zhí)行以避免 GC 搶占 NHRT 的問(wèn)題(參見(jiàn) NHRT 和 RT 線程之間的鎖爭(zhēng)用)。

無(wú)爭(zhēng)用鎖定的性能

非 RT 應(yīng)用程序的標(biāo)準(zhǔn)檢查和檢測(cè)已表明鎖定主要是無(wú)爭(zhēng)用的。無(wú)爭(zhēng)用鎖定同樣被認(rèn)為是 RT 應(yīng)用程序中的主要類(lèi)型,特別是現(xiàn)有組件或庫(kù)需要重用的時(shí)候。如果已知鎖定是無(wú)爭(zhēng)用的但是難以避免或刪除同步指令,則對(duì)這些鎖定花費(fèi)一點(diǎn)小的確定的開(kāi)銷(xiāo)不失為明智的做法。

如前所述,無(wú)爭(zhēng)用鎖定操作涉及了一些設(shè)置和一個(gè)原子機(jī)器指令。解鎖操作涉及一個(gè)原子機(jī)器指令。鎖定操作的設(shè)置涉及分配一個(gè)預(yù)先鎖定的互斥鎖。該分配被認(rèn)為是無(wú)爭(zhēng)用鎖定操作中最大的可變開(kāi)銷(xiāo)。RealtimeSystem.setMaximumConcurrentLocks() 可以幫助控制這種可變開(kāi)銷(xiāo)。

RealtimeSystem.setMaximumConcurrentLocks(int numLocks) 使 WebSphere Real Time 中的 VM 將 numLocks 互斥鎖預(yù)先分配給全局鎖緩存。全局鎖緩存將其內(nèi)容提供給單線程鎖緩存。通過(guò)使用這個(gè) RealTimeSystem API,可以降低具有嚴(yán)格時(shí)間要求的代碼區(qū)域中發(fā)生鎖定初始化的機(jī)率。RealTimeSystem.getMaximumConcurrentLocks() 可以用來(lái)幫助決定 setMaximumConcurentLocks() 調(diào)用中應(yīng)該使用的數(shù)量,但是要注意 getMaximumConcurrentLocks() 提供關(guān)于調(diào)用的鎖使用,而不是最高使用標(biāo)記(high-water mark)。未來(lái)的 RTSJ 版本可能提供 API 以便提供最高使用標(biāo)記。不要為 numLocks 的值提供很大的值,因?yàn)檎{(diào)用 setMaximimConcurrentLocks() 可能耗費(fèi)過(guò)量的時(shí)間和內(nèi)存來(lái)創(chuàng)建那些鎖。還要注意:這個(gè) API 是定義為 JVM 專(zhuān)用的,因此其他 JVM 可能忽略該調(diào)用或提供不同的行為。

爭(zhēng)用鎖定的性能

一個(gè)線程可以同時(shí)持有多個(gè)鎖,并且可能已經(jīng)按某種順序獲得了這些鎖。所有此類(lèi)鎖定模式形成了一個(gè)鎖層次結(jié)構(gòu)。優(yōu)先級(jí)繼承可以指提高和降低一組線程的優(yōu)先級(jí)。組中線程的數(shù)量應(yīng)該不大于系統(tǒng)中最深的鎖層次結(jié)構(gòu)的深度。通過(guò)保持較淺的鎖層次結(jié)構(gòu),可以鎖定最少量的對(duì)象,您能夠影響最大量的需要調(diào)整優(yōu)先級(jí)的線程。

同步操作中的時(shí)間

Object.wait(long timeout, int nanos) 為相關(guān)的等待操作提供納秒粒度。HighResolutionTime.waitForObject() API 與 Object.wait() 類(lèi)似并提供可使用納秒粒度指定的相對(duì)時(shí)間和絕對(duì)時(shí)間。在 WebSphere Real Time 中,這兩個(gè) API 都使用底層 POSIX 鎖定等待服務(wù)來(lái)實(shí)現(xiàn)。這些底層服務(wù)最多提供微秒粒度。如有需要,出于可移植性目的,javax.realtime 包的 Clock 類(lèi)的 getResolution() 方法應(yīng)用于檢索執(zhí)行平臺(tái)的分辨率。





回頁(yè)首


結(jié)束語(yǔ)

RTSJ 通過(guò) javax.realtime 包中的新 RT 類(lèi)和 API 擴(kuò)展并加強(qiáng)了 Java 編程人員的線程化和同步功能。在 WebSphere Real Time 中,這些功能通過(guò) Linux 內(nèi)核的 RT 版本來(lái)實(shí)現(xiàn),對(duì) POSIX 線程化進(jìn)行修改并對(duì) JVM 自身進(jìn)行修改。您現(xiàn)在對(duì) RTSJ 線程化和同步有了更深入的了解,這些知識(shí)可以幫助您在編寫(xiě)和部署 RT 應(yīng)用程序時(shí)避免問(wèn)題的發(fā)生。



參考資料

學(xué)習(xí)

獲得產(chǎn)品和技術(shù)
  • WebSphere Real Time:WebSphere Real Time 利用了標(biāo)準(zhǔn)的 Java 技術(shù)并且沒(méi)有損失確定性,使應(yīng)用程序依賴于精確的響應(yīng)時(shí)間。

  • Real-time Java 技術(shù):訪問(wèn)作者的 IBM alphaWorks 研究站點(diǎn),查找用于實(shí)時(shí) Java 的先進(jìn)技術(shù)。


討論


作者簡(jiǎn)介

 

Patrick Gallop 于 1984 年畢業(yè)于 Waterloo 大學(xué)并獲得數(shù)學(xué)碩士學(xué)位。之后不久就加入了 IBM 多倫多實(shí)驗(yàn)室并從事各種編譯器和編譯器工具項(xiàng)目,包括用于不同操作系統(tǒng)和架構(gòu)的 C 和靜態(tài) Java 編譯器。Patrick 致力于多個(gè)版本的 IBM Java 項(xiàng)目開(kāi)發(fā),最近成為 IBM Real-time Java 項(xiàng)目的高級(jí)開(kāi)發(fā)人員。


Mark stoodley

Mark Stoodley 在 2001 年從多倫多大學(xué)獲得計(jì)算機(jī)工程的博士學(xué)位,并于 2002 年加入 IBM 多倫多實(shí)驗(yàn)室,研究 Java JIT 編譯技術(shù)。從 2005 年起,通過(guò)采用現(xiàn)有的 JIT 編譯器并在實(shí)時(shí)環(huán)境中進(jìn)行操作,他開(kāi)始為 IBM WebSphere Real Time 開(kāi)發(fā) JIT 技術(shù)。他現(xiàn)在是 Java 編譯控制團(tuán)隊(duì)的團(tuán)隊(duì)負(fù)責(zé)人,從事在本地代碼的執(zhí)行環(huán)境中提高本地代碼編譯效率的工作。工作以外,他喜歡收拾自己的家。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多