【发布时间】:2012-11-23 13:05:48
【问题描述】:
我阅读了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()之后)。
我的问题:
如果我是正确的,当从
myThreadClass以外的其他线程访问mHandler时,必须保证最新的数据可见性,因为它不是final字段。我也不想让它成为volatile,因为除了这几个sendMessageWithHandler(..)调用之外,没有同步的其他线程不会使用mHandler(我不希望volatile开销不必要地出现在哪里这是不必要的)。换句话说,当通过useHandlerInAnotherWay()从那些其他线程访问mHandler时,带有“有用代码”(即实际上需要成为互斥对象的代码)的synchronized也保证调用者线程正确地看到mHandler。但是在sendMessageWithHandler(..)中,代码不需要互斥,所以我决定在sendMessageWithHandler(...)的开头放一个空的同步块。它是否正确?我的问题有更好的解决方案吗?-
我链接到的另一个 stackoverflow 线程有以下答案(它不是被接受的,但被多次投票):
过去的情况是规范暗示了一定的记忆 发生了屏障操作。但是,规格现在已经改变,并且 原始规范从未正确实施。它可以用来等待 让另一个线程释放锁,但协调 其他线程已经获得锁会很棘手。
这是否意味着空的
synchronized不再提供内存屏障功能?如果我在线查看有关synchronized的Java 文档,他们提到所有内存都因此而更新(即线程副本在监视器输入时从“主内存”更新,而“主内存”在监视器退出时从线程副本更新)。但是他们没有提到空的synchronized块,所以我不清楚。
【问题讨论】:
-
看看docs.oracle.com/javase/7/docs/api/java/util/concurrent/…。也许信号量或屏障更适合您的用例。
-
感谢您的反馈。我的软件很复杂,这里没有列出一些部分,所以请假设我需要对我所询问的功能有所了解。我开发的组件必须比 Semaphore 或 CycleBarrier 提供的更通用。
-
如果你的意思也是我所说的“每个其他线程调用 sendMessageWithHandler 恰好一次”,那么让我们忽略“恰好一次”。这里只是为了强调该方法不经常使用,因此线程获取自己的 mHandler 副本是没有意义的。
-
谢谢,非常有用的阅读!
标签: java android multithreading synchronized