關於解決 Java 程式語言執行緒問題的建議(3)(轉)

ba發表於2007-08-15
關於解決 Java 程式語言執行緒問題的建議(3)(轉)[@more@]修改 Thread 類

同時支援搶佔式和協作式執行緒的能力在某些伺服器應用程式中是基本要求,尤其是在想使系統達到最高效能的情況下。我認為
Java 程式語言在簡化執行緒模型上走得太遠了,並且 Java 程式語言應支援
Posix/Solaris 的“綠色(green)執行緒”和“輕便(lightweight)程式”概念(在“(Taming Java Threads”第一章中討論)。
這就是說,有些 Java 虛擬機器的實現(例如在 NT 上的 Java 虛擬機器)應在其內部模擬協作式程式,其它
Java 虛擬機器應模擬搶佔式執行緒。而且向 Java 虛擬機器加入這些擴充套件是很容易的。



一個 Java 的 Thread 應始終是搶佔式的。這就是說,一個 Java 程式語言的執行緒應像 Solaris
的輕便程式一樣工作。 Runnable 介面可以用於定義一個 Solaris 式的“綠色執行緒”,此執行緒必需能把控制權轉給執行
在相同輕便程式中的其它綠色執行緒。




例如,目前的語法:




class My_thread implements Runnable
{ public void run(){ /*...*/ }
}

new Thread( new My_thread );





能有效地為 Runnable 物件產生一個綠色執行緒,並把它繫結到由
Thread 物件代表的輕便程式中。這種實現對於現有程式碼是透明的,因為它的有效性和現有的完全一樣。


把 Runnable 物件想成為綠色執行緒,使用這種方法,只需向
Thread 的建構函式傳遞幾個 Runnable物件,就可以擴充套件
Java 程式語言的現有語法,以支援在一個單一輕便執行緒有多個綠色執行緒。(綠色執行緒之間可以相互協作,但是它們可被執行在其它輕便程式
(Thread 物件) 上的綠色程式(Runnable 物件) 搶佔。)。例如,下面的程式碼會為每個
runnable 物件建立一個綠色執行緒,這些綠色執行緒會共享由 Thread 物件代表的輕便程式。


new Thread( new My_runnable_object(), new My_other_runnable_object() );





現有的覆蓋(override) Thread 物件並實現 run() 的習慣繼續有效,但是它應對映到一個被繫結到一輕便程式的綠色執行緒。(在
Thread() 類中的預設 run() 方法會在內部有效地建立第二個 Runnable 物件。)

執行緒間的協作

應在語言中加入更多的功能以支援執行緒間的相互通訊。目前,PipedInputStream
和 PipedOutputStream 類可用於這個目的。但是對於大多數應用程式,它們太弱了。我建議向
Thread 類加入下列函式:


增加一個 wait_for_start() 方法,它通常處於阻塞狀態,直到一個執行緒的
run() 方法啟動。(如果等待的執行緒在呼叫 run 之前被釋放,這沒有什麼問題)。用這種方法,一個執行緒可以建立一個或多個輔助執行緒,並保證在建立執行緒繼續執行操作之前,這些輔助執行緒會處於執行狀態。


(向 Object 類)增加 $send (Object o) 和 Object=$receive()
方法,它們將使用一個內部阻斷佇列線上程之間傳送物件。阻斷佇列應作為第一個
$send() 呼叫的副產品被自動建立。 $send() 呼叫會把物件加入佇列。
$receive() 呼叫通常處於阻塞狀態,直到有一個物件被加入佇列,然後它返回此物件。這種方法中的變數應支援設定入隊和出隊的操作超時能力:
$send (Object o, long timeout)
和 $receive (long timeout)。



對於讀寫鎖的內部支援

讀寫鎖的概念應內建到 Java 程式語言中。讀寫器鎖在“Taming Java Threads”(和其它地方)中有詳細討論,概括地說:一個讀寫鎖支援多個執行緒同時訪問一個物件,但是在同一時刻只有一個執行緒可以修改此物件,並且在訪問進行時不能修改。讀寫鎖的語法可以借用 synchronized 關鍵字:

static Object global_resource;

//...

public void a()
{
$reading( global_resource )
{ // While in this block, other threads requesting read
// access to global_resource will get it, but threads
// requesting write access will block.
}
}

public void b()
{
$writing( global_resource )
{ // Blocks until all ongoing read or write operations on
// global_resource are complete. No read or write
// operation or global_resource can be initiated while
// within this block.
}
}

public $reading void c()
{ // just like $reading(this)...
}

public $writing void d()
{ // just like $writing(this)...
}





對於一個物件,應該只有在 $writing 塊中沒有執行緒時,才支援多個執行緒進入
$reading 塊。在進行讀操作時,一個試圖進入 $writing
塊的執行緒會被阻斷,直到讀執行緒退出 $reading
塊。 當有其它執行緒處於 $writing 塊時,試圖進入 $reading
或 $writing 塊的執行緒會被阻斷,直到此寫執行緒退出 $writing
塊。



如果讀和寫執行緒都在等待,預設情況下,讀執行緒會首先進行。但是,可以使用
$writer_priority 屬性修改類的定義來改變這種預設方式。如:



$write_priority class IO
{
$writing write( byte[] data )
{ //...
}

$reading byte[] read( )
{ //...
}
}


訪問部分建立的物件應是非法的

當前情況下,JLS 允許訪問部分建立的物件。例如,在一個建構函式中建立的執行緒可以訪問正被建立的物件,既使此物件沒有完全被建立。下面程式碼的結果無法確定:


class Broken
{ private long x;

Broken()
{ new Thread()
{ public void run()
{ x = -1;
}
}.start();

x = 0;
}
}




設定 x 為 -1 的執行緒可以和設定 x 為 0 的執行緒同時進行。所以,此時 x 的值無法預測。


對此問題的一個解決方法是,在建構函式沒有返回之前,對於在此建構函式中建立的執行緒,既使它的優先順序比呼叫
new 的執行緒高,也要禁止執行它的 run() 方法。




這就是說,在建構函式返回之前, start()
請求必須被推遲。

另外,Java 程式語言應可允許建構函式的同步。換句話說,下面的程式碼(在當前情況下是非法的)會象預期的那樣工作:

class Illegal
{ private long x;

synchronized Broken()
{ new Thread()
{ public void run()
{ synchronized( Illegal.this )
{
x = -1;
}
}
}.start();

x = 0;
}
}






我認為第一種方法比第二種更簡潔,但實現起來更為困難。


volatile
關鍵字應象預期的那樣工作

JLS 要求保留對於 volatile 操作的請求。大多數 Java 虛擬機器都簡單地忽略了這部分內容,這是不應該的。在多處理器的情況下,許多主機都出現了這種問題,但是它本應由
JLS 加以解決的。如果您對這方面感興趣,馬里蘭大學的 Bill Pugh
正在致力於這項工作(請參閱參考資料)。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617731/viewspace-958391/,如需轉載,請註明出處,否則將追究法律責任。

相關文章