2012-01-09 23 views
5

我正在使用一些使用​​塊的CMU Sphinx語音識別器庫(Link to source)。從RecognizerTask多線程的正確性:使用同步塊

一個例子塊:

Event mailbox; 

[...] 

public void start() { 
    synchronized (this.mailbox) { 
     this.mailbox.notifyAll(); 
     this.mailbox = Event.START; 
    } 
} 

代碼工作沒有任何問題,但是BugFinder給出了這樣的警告:

錯誤:同步上RecognizerTask.mailbox在妄圖 後衛它

此方法在似乎是 嘗試防止同時發生的字段上同步更新該字段。但是 守護字段會鎖定引用的對象,而不是在 字段上。這可能不會提供您需要的互斥,而其他線程可能正在獲取對引用對象的鎖定(對於其他 目的)。這種模式的一個例子是:

private Long myNtfSeqNbrCounter = new Long(0); 
private Long getNotificationSequenceNumber() { 
    Long result = null; 
    synchronized(myNtfSeqNbrCounter) { 
     result = new Long(myNtfSeqNbrCounter.longValue() + 1); 
     myNtfSeqNbrCounter = new Long(result.longValue()); 
    } 
    return result; 
} 

說實話,我不太明白的錯誤描述,什麼應該是錯在這種情況下。 是一個全局變量而不是一個字段?如果不是,我該如何改進代碼?

/編輯:這是Event.wait()被稱爲僅部分:

Event todo = Event.NONE; 
     synchronized (this.mailbox) { 
      todo = this.mailbox; 
      /* If we're idle then wait for something to happen. */ 
      if (state == State.IDLE && todo == Event.NONE) { 
       try { 
        //Log.d(getClass().getName(), "waiting"); 
        this.mailbox.wait(); 
        todo = this.mailbox; 
        //Log.d(getClass().getName(), "got" + todo); 
       } catch (InterruptedException e) { 
        /* Quit main loop. */ 
        //Log.e(getClass().getName(), "Interrupted waiting for mailbox, shutting down"); 
        todo = Event.SHUTDOWN; 
       } 
      } 
      /* Reset the mailbox before releasing, to avoid race condition. */ 
      this.mailbox = Event.NONE; 
     } 

此代碼實際上是使用​​聲明爲好。使用它有意義嗎?

回答

3

我不認爲它適用於你的情況。你必須notifyAll()通話,這意味着在其他線程的代碼某處有一個匹配wait()電話:

synchronized (this.mailbox) { 
    this.mailbox.wait();   
} 

這意味着在等待通知其他線程將放棄對鎖。

您的代碼檢查可能是由線路困惑:

this.mailbox = Event.START; 

這意味着你可能同時修改此對象,例如,如果另一個線程試圖獲得this.mailbox鎖,它會看到一個不同的對象。不過,我認爲既然:

  1. this.mailbox是全局可見
  2. 引用的受讓人是原子
  3. 鎖產生一個籬笆

所有線程應該有同步的更新視圖對象在任何時候。

+0

你是對的。警告是正確的,同步不會做大多數人會認爲它做的事。但在這種情況下,這並不重要。線程只是在一個無關緊要的地方持有'錯誤的'鎖 - '不正確'鎖定的代碼不需要鎖定,因爲它會自動分配一個引用。 – 2012-01-09 11:13:50

+0

非常感謝您的回答!請看我更新的問題 - 你會建議完全刪除'synchronized'嗎? – Force 2012-01-09 16:44:46

+1

實際上,你不能這樣做,因爲對象上的等待或通知/ notifyAll調用必須位於同一個對象上的同步塊內。 – Tudor 2012-01-09 20:08:23

3

同步模塊「捕獲」給定對象的鎖,對於mailbox表示的對象。一旦將變量mailbox更改爲指向另一個對象,其他線程就可以「捕獲」該對象的鎖而不會出現問題,因爲它不會被採用。

請注意,該鎖是用於對象,而不是引用!

現在,海外商品會有下面的[僞代碼]:

synchronised (myObject) { 
    myObject = new Object(); 
    i += 5; //assume i is an instance variable 
} 

實際上這裏沒有鎖!每個線程都在鎖塊中創建一個新對象,並且對i的修改不同步!

+1

這就是爲什麼我們需要創建一個最終對象並將其用於鎖定。 – 2012-01-09 12:15:17

+0

非常感謝您的回答!請看我更新的問題 - 你會建議完全刪除'synchronized'嗎? – Force 2012-01-09 16:44:41