我讀this計算器主題,結論似乎是一個空塊可以總是一個更好的解決方案來避免。這個話題對我來說也有一些不清楚的部分,我將整合到我的下面的文章中。空同步塊:數據可視性
假設我們有一類這樣的:
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()
被訪問時,與該「有用的代碼」(即,實際上需要是相互排斥的受試者碼)那裏也保證了呼叫者線程看到mHandler
正確。在sendMessageWithHandler(..)
,然而,代碼不需要相互排斥,所以我決定把一個空同步塊的sendMessageWithHandler(...)
開始。它是否正確?我的問題有更好的解決方案嗎?其他計算器線程我掛有如下的答案(它不是接受一個,但upvoted多次):
它曾經是說明書暗示某些內存 屏障的情況下操作發生。但是,規範現在已經改變,並且原始規範從未正確實施。它可以用來等待 另一個線程釋放鎖,但協調的 其它線程已經獲得鎖將是棘手的。
這是否意味着空的不再提供內存屏障功能?如果我在線查看關於的Java文檔,他們會提到所有內存都會因爲它而更新(即線程副本從顯示器輸入處的「主內存」更新,並且「主內存」從顯示器出口處的線程副本更新)。但他們沒有提及任何關於空的塊,所以這對我來說還不清楚。
查看http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html。也許信號量或障礙對你的用例更好。 – ssedano
感謝您的反饋。我的軟件非常複雜,部分內容沒有在這裏列出,所以請讓我們假設我需要通用我所問的功能。我開發的組件必須比Semaphore或CycleBarrier提供的更通用。 –
如果您也意味着我的聲明「每個其他線程只調用一次sendMessageWithHandler *」,那麼讓我們忽略「恰好一次」。只是爲了強調該方法不經常使用,因此線程無法獲得自己的mHandler副本是沒有意義的。 –