繁体   English   中英

重新访问空的同步块:数据可见性

[英]Empty synchronized block revisited: data visibility

我读了这个 stackoverflow主题,结论似乎是一个空的synchronized块总是可以避免使用更好的解决方案。 这个话题对我来说也有一些不清楚的部分,我将整合到我的下面的帖子中。

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

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()当然在mOtherComponent.onMyCustomThreadInitialized()之后)。

我的问题:

  1. 如果我是正确的,最多时最新数据可见性必须得到保证mHandler从其他线程访问比myThreadClass ,因为它不是一个final场。 我也不想让它变得volatile ,因为除了这几个sendMessageWithHandler(..)调用之外, mHandler没有在没有同步的情况下从其他线程中使用(我不希望在不必要的地方不必要地存在volatile开销)。 换句话说,当mHandler已经从那些还没有其他线程通过访问useHandlerInAnotherWay()时, synchronized的“有用的代码”(即实际需要的是相互排斥的对象,即代码)存在也保证了呼叫者线程看到mHandler正确。 但是,在sendMessageWithHandler(..) ,代码不需要互斥,所以我决定将一个空的synchronized块放到sendMessageWithHandler(...)的开头。 这个对吗? 我的问题有更好的解决方案吗?

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

    过去,规范暗示某些内存屏障操作发生。 但是,规范现在已经改变,原始规范从未正确实现。 它可能用于等待另一个线程释放锁,但协调另一个线程已经获得锁定将是棘手的。

    这是否意味着空synchronized不再提供内存屏障功能? 如果我在线查看关于synchronized的Java文档,他们会提到由于它而更新了所有内存(即,在监视器输入时从“主存储器”更新线程副本,并且在监视器出口处从“线程副本”更新“主存储器”)。 但是他们没有提到关于空synchronized块的任何事情,所以我不清楚这一点。

您可能不需要任何同步。

(thread 1)          (thread 2)

write
  |
start thread 2
                 \
                    ...
                     |
                    read  

read保证看到write


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

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

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

由于“其他每个线程都会调用sendMessageWithHandler(...)一次”,因此开销几乎可以忽略不计。 坦率地说,你对最小化同步设施的使用(“我不想让它变得易失”)的痴迷在多个方法调用和线程创建的背景下看起来不合适。 只有当同步每秒发生数百万次时才值得打扰。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM