我阅读了这个synchronized
stackoverflow 主题,其结论似乎是始终可以通过更好的解决方案来避免空块。这个话题对我来说也有一些不清楚的部分,我将把它们整合到我的下面的帖子中。
假设我们有一个这样的类:
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(...);
}
}
我的应用程序的相关部分按以下方式工作:
MyThreadClass
线程被创建并启动。- 作为间接后果
mOtherComponent.onMyCustomThreadInitialized()
,我的应用程序的另一部分将开始产生其他线程。(请注意,它们不是从这个调用同步启动的,这就是为什么我说它是间接结果。唯一的一点是,在这些其他线程启动mHandler
时已经初始化) - 其他每个线程将只调用
sendMessageWithHandler(...)
一次 - 再一次,其他线程(即不是上面提到的线程)调用
useHandlerInAnotherWay(...)
,这可能随时发生(mOtherComponent.onMyCustomThreadInitialized()
当然是在 之后)。
我的问题:
mHandler
如果我是正确的,当从其他线程访问时必须保证最新的数据可见性myThreadClass
,因为它不是一个final
字段。我也不想这样做volatile
,因为除了这几个sendMessageWithHandler(..)
调用之外,mHandler
不会在没有同步的情况下从其他线程中使用(我不希望volatile
开销不必要地出现在不必要的地方)。换句话说,当mHandler
从那些其他线程通过 访问时useHandlerInAnotherWay()
,synchronized
那里的“有用代码”(即实际上需要成为互斥对象的代码)也保证调用者线程mHandler
正确地看到。然而sendMessageWithHandler(..)
,在 中,代码不需要互斥,sendMessageWithHandler(...)
. 它是否正确?我的问题有更好的解决方案吗?我链接到的另一个stackoverflow线程有以下答案(它不是被接受的,但被多次投票):
过去的情况是,规范暗示发生了某些内存屏障操作。但是,规范现在已经更改,原始规范从未正确实施。它可以用来等待另一个线程释放锁,但是协调另一个线程已经获得锁会很棘手。
这是否意味着空
synchronized
不再提供内存屏障功能?如果我在线查看 Java 文档synchronized
,他们提到所有内存都因此而更新(即线程副本在监视器输入时从“主内存”更新,“主内存”在监视器退出时从线程副本更新)。但是他们没有提到任何关于空synchronized
块的内容,所以我不清楚。