2012-01-09 54 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