[英]How to test visibility of values between threads
在線程之間測試價值可見性的最佳方法是什么?
class X {
private volatile Object ref;
public Object getRef() {
return ref;
}
public void setRef(Object newRef) {
this.ref = newRef;
}
}
類X公開了對ref
對象的ref
。 如果並發線程讀取並寫入對象引用,則每個Thread必須查看已設置的最新對象。 volatile
修飾符應該這樣做。 這里的實現是一個示例,它也可以是同步的或基於鎖的實現。
現在我正在尋找一種方法來編寫一個測試,告知我什么時候值可見性沒有達到指定值(讀取舊值)。
如果測試確實會燒掉一些cpu周期,那也沒關系。
如果我理解正確,那么你想要一個測試用例,如果變量被定義為volatile而傳遞,如果沒有則失敗。
但是我認為沒有可靠的方法來做到這一點。 根據jvm並發訪問的實現,即使沒有volatile,也可以正常工作。
因此,如果指定了volatile,單元測試將正常工作,但仍然可以在沒有volatile的情況下正常工作。
JLS說明了為了在涉及“線程間操作”的應用程序中獲得有保證的一致執行,您應該做些什么。 如果您不執行這些操作,則執行可能會不一致。 但是它實際上是否會不一致取決於您使用的JVM,您正在使用的硬件,應用程序,輸入數據以及......運行應用程序時機器上可能發生的任何其他情況。
我看不出你提出的測試會告訴你什么。 如果測試顯示執行不一致,則會確認正確進行同步的智慧。 但是如果運行測試幾次只顯示(顯然)一致的執行,這並不能告訴你執行總是一致的。
例:
假設您在(例如)JDK 1.4.2(rev 12)/ Linux / 32bit上運行測試,其中“客戶端”JVM和選項x,y,z在單個處理器計算機上運行。 在運行測試1000次后,您會發現如果省略不 volatile
, 它似乎沒有任何區別 。 你在那種情況下實際學到了什么?
volatile
關鍵字。 如果測試顯示出差異,您只能學到一些東西。 而你所學到的唯一一點是同步很重要 ......這就是所有教科書等一直在告訴你的事情:-)
底線:這是最糟糕的黑盒測試。 它沒有讓你真正了解盒子里面發生了什么。 要獲得這種洞察力,您需要1)了解內存模型和2)深入分析JIT編譯器發出的本機代碼(在多個平台上......)
哇,這比我最初的想法要難得多。 我可能會完全離開,但是這個怎么樣?
class Wrapper {
private X x = new X();
private volatile Object volatileRef;
private final Object setterLock = new Object();
private final Object getterLock = new Object();
public Object getRef() {
synchronized(getterLock) {
Object refFromX = x.getRef();
if (refFromX != volatileRef) {
// FAILURE CASE!
}
return refFromX;
}
}
public void setRef(Object ref) {
synchronized(setterLock) {
volatileRef = ref;
x.setRef(ref);
}
}
}
這有用嗎? 當然,你必須創建許多線程來命中這個包裝器,希望出現壞的情況。
這個怎么樣 ?
public class XTest {
@Test
public void testRefIsVolatile() {
Field field = null;
try {
field = X.class.getDeclaredField("ref");
} catch (SecurityException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
} catch (NoSuchFieldException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
Assert.assertNotNull("Ref field", field);
Assert.assertTrue("Is Volatile", Modifier.isVolatile(field
.getModifiers()));
}
}
所以基本上你想要這個場景:一個線程寫入變量,而另一個線程同時讀取它,並且你想確保變量read具有正確的值,對吧?
好吧,我不認為你可以使用單元測試,因為你無法確保合適的環境。 這是由JVM完成的,它是如何調度指令的。 這就是我要做的。 使用調試器。 啟動一個線程來寫入數據並在執行此操作的行上放置一個斷點。 啟動第二個線程並讓它讀取數據,同時停止。 現在,執行第一個線程來執行寫入的代碼,然后使用第二個線程進行讀取。 在您的示例中,您將無法實現此目的,因為讀取和寫入是單個指令。 但通常如果這些操作更復雜,您可以交替執行兩個線程並查看是否一切都是一致的。
這需要一些時間,因為它不是自動化的。 但我不會去寫一個單元測試,嘗試讀寫很多次,希望能找到失敗的情況,因為你不會做任何事情。 單元測試的作用是確保您編寫的代碼按預期工作。 但是在這種情況下,如果測試通過,你就不會確定是否有任何感覺。 也許這只是幸運的,沖突並沒有出現在這次運行中。 這就失敗了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.