簡體   English   中英

兩個線程更新同一個對象,它會起作用嗎? 爪哇

[英]Two threads updating the same object, will it work? java

我有以下兩種方法:

class A{
  void method1(){
    someObj.setSomeAttribute(true);
    someOtherObj.callMethod(someObj);
  }
  void method2(){
    someObj.setSomeAttribute(false);
    someOtherObj.callMethod(someObj);
  }
}

在另一個地方評估該屬性的位置:

class B{
  void callMethod(Foo someObj){
    if(someObj.getAttribute()){
        //do one thing

    } else{
        //so another thing
    }
  }
}

請注意, A.method1A.method2正在更新同一對象的屬性。 如果這 2 個方法在 2 個線程中運行,這會起作用還是會出現意外結果?

會不會有意想不到的結果? 是的,保證,因為如果你修改了你不希望對你的應用程序產生影響的東西(例如月相,你的 winamp 中播放的當前歌曲,你的狗是否在 CPU 附近擁抱,如果它是月的第 5 個星期二,以及其他類似的事情),這可能會對行為產生影響。 你不想要的。

您所描述的是所謂的對 java 內存模型的違反:最終結果是任何 java 實現都可以自由返回多個值中的任何一個,但是,該 VM 根據 java 規范正常運行。 即使它看起來是隨意的。

作為一般規則,每個線程都會得到一個不公平的硬幣。 不公平,因為它會試圖惹你:每次測試時它都會正確翻轉,然后在生產中,只有當你向那個關鍵客戶進行演示時,它才會得到你。

每次它從任何字段讀取或寫入時,它都會翻轉這個平均硬幣。 在頭上,它將使用實際字段。 在尾部,它將使用它制作的本地副本。

這有點過於簡化了模型,但這是嘗試了解其工作原理的良好開端。

出路是強制所謂的“先於”關系:java 會做的是確保您可以觀察到的內容與這些關系匹配:如果事件 A 被定義為具有先於關系與事件 B,那么任何事情A 確實會被觀察到,B 保證完全按照原樣。 沒有更多的硬幣翻轉。

建立優先關系的例子包括使用volatilesynchronized和任何在內部使用這些東西的方法。

注意:當然。 如果您沒有粘貼的setSomeAttribute方法包含一些先於建立行為,那么這里沒有問題,但通常稱為setX的方法不會這樣做。

一個沒有的例子:

class Example {
  private String attr;

  public void setAttr(String attr) {
    this.attr = attr;
  }
}

一些這樣做的例子:

  • 比方說,法B.callMethod在同一個線程中執行method1 -那你都保證至少觀察變化做出方法1,但它仍然是一個拋硬幣(不管你實際看到有什么方法2.沒有或沒有)。 不可能在method1 或method2 運行之前看到該屬性的值,因為在單個線程中運行的代碼在整個運行中都出現了(在同一線程中的另一行之前執行的任何行都有一個出現-關系之前)。

  • set 方法如下所示:

class Example {
  private String attr;
  private final Object lock = new Object();

  public void setAttr(String attr) {
    synchronized (lock) {
      this.attr = attr;
    }
  }

  public String getAttr() {
    synchronized (lock) {
      return this.attr;
    }
  }
}

現在 get 和 set ops 鎖定在同一個對象上,這是建立優先的方法之一。 哪個線程首先獲得鎖是可觀察到的行為; 如果method1的set在B的get之前到達那里,那么你一定會觀察到method1的set。

更一般地說,線程之間共享狀態非常棘手,您應該盡量不要這樣做。 替代方案是:

  • 在啟動線程之前初始化所有狀態,然后執行工作,並且只有在完成后,才將所有結果傳回。 Fork/join 就是這樣做的。
  • 使用具有良好並發基礎的消息傳遞系統,例如具有事務或消息隊列庫的數據庫。
  • 如果您必須共享狀態,請嘗試根據 juconcurrent 中的好類來編寫內容。

我假設您期望的是當您調用A.method1someObj.getAttribute()將在B.callMethod返回true ,當您調用A.method2someObj.getAttribute()將在B.callMethod返回false

不幸的是,這行不通。 因為在setSomeAttributecallMethod行之間,其他線程可能會更改該屬性的值。

如果你只在callMethod使用屬性,為什么不直接傳遞屬性而不是Foo對象。 代碼如下:

class A{
  void method1(){
    someOtherObj.callMethod(true);
  }
}
class B{
  void callMethod(boolean flag){
    if(flag){
        //do one thing
    } else{
        //so another thing
    }
  }
}

如果必須使用Foo作為參數,你可以做的就是讓setAttributecallMethod原子化。
實現它最簡單的方法是使其同步。代碼如下:

  synchronized void method1(){
    someObj.setSomeAttribute(true);
    someOtherObj.callMethod(someObj);
  }
  synchronized void method2(){
    someObj.setSomeAttribute(false);
    someOtherObj.callMethod(someObj);
  }

但這可能會帶來糟糕的性能,您可以使用一些更細粒度的鎖來實現它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM