我读了this 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(...);
}
}
我的申请的相关部分以下列方式运作:
MyThreadClass
线程。mOtherComponent.onMyCustomThreadInitialized()
的间接结果,我的应用程序的另一部分将开始产生其他线程。 (注意它们不是从这个调用同步启动的,这就是为什么我说这是间接后果。唯一的一点是,mHandler
已经被这些其他线程已开始)sendMessageWithHandler(...)
useHandlerInAnotherWay(...)
,这可能在任何时候发生(当然在mOtherComponent.onMyCustomThreadInitialized()
之后)。我的问题:
如果我是正确的,那么从mHandler
以外的其他线程访问myThreadClass
时,必须保证最新的数据可见性,因为它不是final
领域。我也不想让它volatile
,因为除了这几个sendMessageWithHandler(..)
调用之外,没有同步的其他线程没有使用mHandler
(我不想{{1}在不必要的地方不必要地存在开销。换句话说,当通过volatile
从那些其他线程访问mHandler
时,那里的useHandlerInAnotherWay()
带有“有用代码”(即实际上需要成为互斥主题的代码) )还保证调用者线程正确地看到synchronized
。但是,在mHandler
中,代码不需要互斥,因此我决定将空的同步块放到sendMessageWithHandler(..)
的开头。它是否正确?我的问题有更好的解决方案吗?
我链接到的另一个stackoverflow线程有以下答案(它不是被接受的,但被多次投票):
以前是规范暗示某些记忆的情况 障碍行动发生了。但是,规范现在已经改变了 原始规格从未正确实施。它可能用于等待 另一个线程释放锁,但协调该 其他线程已经获得锁定将是棘手的。
这是否意味着空sendMessageWithHandler(...)
不再提供内存屏障功能?如果我在线查看关于synchronized
的Java文档,他们会提到所有内存都会因为它而更新(即在监视器输入时从“主内存”更新线程副本,并且从主机内容更新“主内存”)监控退出)。但是他们没有提到关于空synchronized
块的任何内容,所以我不清楚这一点。
答案 0 :(得分:2)
您可能不需要任何同步。
(thread 1) (thread 2)
write
|
start thread 2
\
...
|
read
read
保证会看到write
。
synchronized
的语义是严格执行的,即使它是一个空块。
答案 1 :(得分:1)
正如我所看到的,mHandler在创建之前不会被其他线程访问,并且在生命周期内不会被更改。因此,可以安全地从其他线程读取它而无需任何同步。为了绝对确定,您可以在synchronized块中读取它:
public void sendMessageWithHandler(int what, int arg) {
MyHandler mHandler;
synchronized (mLock) {
mHandler=this.mHandler;
}
// the rest of code unchanged
}
由于“其他每个线程都会调用sendMessageWithHandler(...)
一次”,因此开销几乎可以忽略不计。坦率地说,你对最小化同步设施的使用(“我不想让它变得易失”)的痴迷在多个方法调用和线程创建的背景下看起来不合适。只有当同步每秒发生数百万次时才值得打扰。