1

我阅读了这个synchronizedstackoverflow 主题,其结论似乎是始终可以通过更好的解决方案来避免空块。这个话题对我来说也有一些不清楚的部分,我将把它们整合到我的下面的帖子中。

假设我们有一个这样的类:

public class MyThreadClass extends Thread {
  private final OtherComponent mOtherComponent;
  private final Object mLock = new Object();
  private MyHandler mHandler;

  public MyCustomThread(OtherComponent otherComponent) {
      mOtherComponent = otherComponent;      

  public void run() {

      mHandler = new Handler() {...}

      // Other init operations

      mOtherComponent.onMyCustomThreadInitialized();

      // ... other operations (Looper.loop(), actually)
  }

  public void sendMessageWithHandler(int what, int arg) {
       synchronized (mLock) {}
       Message msg = mHandler.obtainMessage(what);
       msg.arg1 = arg;
       mHandler.sendMessage(msg);
  }

  public void useHandlerInAnotherWay(...) {
       synchronized (mLock) {
            // useful code that needs actual mutual exclusion
       }
       mHandler.sendMessage(...);
  }
}

我的应用程序的相关部分按以下方式工作:

  1. MyThreadClass线程被创建并启动。
  2. 作为间接后果mOtherComponent.onMyCustomThreadInitialized(),我的应用程序的另一部分将开始产生其他线程。(请注意,它们不是从这个调用同步启动的,这就是为什么我说它是间接结果。唯一的一点是,在这些其他线程启动mHandler时已经初始化
  3. 其他每个线程将只调用sendMessageWithHandler(...)一次
  4. 再一次,其他线程(即不是上面提到的线程)调用useHandlerInAnotherWay(...),这可能随时发生(mOtherComponent.onMyCustomThreadInitialized()当然是在 之后)。

我的问题:

  1. mHandler如果我是正确的,当从其他线程访问时必须保证最新的数据可见性myThreadClass,因为它不是一个final字段。我也不想这样做volatile,因为除了这几个sendMessageWithHandler(..)调用之外,mHandler不会在没有同步的情况下从其他线程中使用(我不希望volatile开销不必要地出现在不必要的地方)。换句话说,当mHandler从那些其他线程通过 访问时useHandlerInAnotherWay()synchronized那里的“有用代码”(即实际上需要成为互斥对象的代码)也保证调用者线程mHandler正确地看到。然而sendMessageWithHandler(..),在 中,代码不需要互斥,sendMessageWithHandler(...). 它是否正确?我的问题有更好的解决方案吗?

  2. 我链接到的另一个stackoverflow线程有以下答案(它不是被接受的,但被多次投票):

    过去的情况是,规范暗示发生了某些内存屏障操作。但是,规范现在已经更改,原始规范从未正确实施。它可以用来等待另一个线程释放锁,但是协调另一个线程已经获得锁会很棘手。

    这是否意味着空synchronized不再提供内存屏障功能?如果我在线查看 Java 文档synchronized,他们提到所有内存都因此而更新(即线程副本在监视器输入时从“主内存”更新,“主内存”在监视器退出时从线程副本更新)。但是他们没有提到任何关于空synchronized块的内容,所以我不清楚。

4

2 回答 2

2

您可能不需要任何同步。

(thread 1)          (thread 2)

write
  |
start thread 2
                 \
                    ...
                     |
                    read  

read保证看到write


的语义synchronized是严格执行的,即使它是一个空块。

于 2012-12-05T16:39:28.503 回答
1

正如我所看到的, mHandler 在创建之前不会被其他线程访问,并且在生命周期内不会更改。因此,可以安全地从其他线程读取它而无需任何同步。可以肯定的是,您可以在同步块中阅读它:

public void sendMessageWithHandler(int what, int arg) {
   MyHandler mHandler;
   synchronized (mLock) {
      mHandler=this.mHandler;
   }
   // the rest of code unchanged
}

由于“每个其他线程将只调用sendMessageWithHandler(...)一次”,因此开销绝对可以忽略不计。坦率地说,在多个方法调用和线程创建的背景下,您对尽量减少同步工具的使用(“我也不想让它变得易变”)的痴迷看起来不够充分。只有在每秒发生数百万次同步时才值得打扰。

于 2012-12-05T17:07:16.967 回答