譯文連接配接 譯文位址 譯者:衣着時 校對:丁一 (有興趣參與試譯或校對的同學,請加入并發網試譯者qq群:369468545)
java特性
java線程通常是一個帶有run()方法的java.lang.thread的子類,然後調用這個子類對象的start()方法。我們之前定義過,資料競争是因為兩個線程同時通路記憶體單元,在java中,記憶體單元是一個對象字段或數組元素。
由于java旨在支援運作不受信任代碼作為受信任的應用程式的一部分,必須限制不受信任代碼的資料争用造成的破壞。是以不允許資料争用的任意行為,是以,java語言規範包含了一個複雜的規則集,用來定義線程間的共享對象的行為,包括資料争用的行為,這些規則的影響甚至專家都覺得驚訝。然而這些規則保證了免除資料争用的程式的連續一緻,對于程式來講是個更加容易的模型。
如上所述java的資料争用定義的可替換的定義是,并發沖突操作必須被阻止同時出現通過執行相同的線程,或者引入強制實施線程間的順序的同步變量。如果采用了這些機制,就可以說一個記憶體操作發生在另一個記憶體操作之前。是以不會發生交叉存儲。這基本相當于我們的定義。
在幾乎所有情況下,java程式應該避免資料競争,和依賴順序一緻性。事實上附加的保障資料競争問題隻有三種情況:
1.對于編譯器,必須保護它們。
2.對于尤其安全的敏感的代碼,作者需要限制不受信任的“沙箱”代碼引起的破壞。
3.對于極其性能敏感代碼的富有經驗的作者來講,使用同步變量的格外代價太高。盡管這樣的代碼存在于java.util.concurrent類庫裡,我們還是期望比較少的程式員寫這樣的代碼。
java提供了不同尋常方式的鎖:每個java對象都可以作為一個鎖。即邏輯上有個關聯鎖,而不是提供一個顯式的lock()和unlock()函數。java提供了同步塊來獲得和釋放鎖。在指定的代碼塊被執行期間一直保持住鎖。
盡管最近java版本提供了顯式鎖操作(java.util.concurent.locks),同步鎖有實質性的好處,鎖可以沿着代碼塊的方向釋放,其中包括異常被抛出,進而消除錯誤的常見來源。
正如我們上面提及的,同步變量或者更正确的對象字段,通常用volatile關鍵字聲明。由于不是一個單獨的類型,可能有些令人驚訝,結果是:
數組元素不能同步,因為沒有辦法吧數組元素聲明為volatile。
正如我們前面暗示的,volatile僅僅影響個别的記憶體存儲,如果i被聲明為volatile int i,那麼++i包含兩個單獨的不可分割的記憶體通路,即增量作為一個不可分割的整體。
java.util.concurrent.atomic包提供了一些原子類型規避這兩種結果。
java.util.concurrent提供了許多其他的措施支援多線程,包括豐富的類庫用來同步或線程間的互動。
<b>一些c++0x 特征</b>
約定成俗,我們要用術語c++0x指下一個c++标準,盡管我們并不期望它被正式作為一個iso标準直到2010或2011年。一個委員會起草的标準目前是可用的,并且我們期望許多供應商支援早于2011年的部分,在這我們所有的解釋明确地适應2008委員會草案。
和目前(2003)c++标準不同,c++0x明确的支援線程,線程通過std::thread類的執行個體建立,調用構造函數或者執行可調用的對象。
由于c++沒有被設計來提供保護不受信任的代碼的,它無法保證資料争用,允許資料争用的任意程式産生”未定義行為”。
資料争用比較少的時候,提供的一些低層次的類庫措施不能用來提供連續一緻性。正像java,這顯而易見不是官方語言描述,在這種情況下,不使用資料争用而用低層次類庫措施的語言描述更為複雜。
可以通過構造互斥獲得鎖,典型的是std::mutex,然後通過它獲得構造函數std::lock_quard對象,確定在lock_quard析構函數釋放鎖,正如java中被釋放,即使是抛出異常。是以看起來像典型的代碼來獲得一個鎖。
#include <mutex>
std::mutex mtx; // the lock; shared by multiple threads
…
{std::lock_guard _(mtx);
++c;
}
在c++0x中,整形同步變量i可能被聲明為atomic<int> i;
同步變量與普通變量不同有個不同的類型,是以可以提供成員函數的不同實作,如果i像以上一樣被聲明,并發通路確定了正如以前一樣的連續一緻的行為,但是也確定了一個不可分割的操作,++i自動增加。
(c++裡也有volatile,由于曆史原因,這意味着别的。)
目前看來,c++0x模型将在其他環境中,目前迹象表明,下一個c标準将遵循一種方法類似c++0x。openmp似乎也正走向一個類似解決方案。