[英]Is my service thread safe?
我有一個單例類MyService
,它具有兩個功能來讀寫文件中的數據:
public class MyService {
private static MyService instance;
private MyFileAccessor fileAccessor;
private MyService() {
fileAccessor = new MyFileAccessor();
}
public static MyService getInstance() {
if (instance == null) {
instance = new MyService();
}
return instance;
}
// write data to file through fileAccessor object
public void writeDataToFile(Object data){
fileAccessor.writeToFile(data);
}
// read data from file through fileAccessor object
public Object readFile() {
return fileAccessor.readFromFile();
}
}
MyFileAccessor
類具有同步功能以讀取和寫入文件:
public class MyFileAccessor {
private File mFile;
public MyFileAccessor() {
mFile = new File(PATH);
}
public synchronized Object readFromFile() {
// code to read mFile
…
}
public synchronized void writeToFile(Object data) {
// code to write data to mFile
…
}
}
我的問題:當我的項目通過它讀寫數據時,單例MyService
類是否是線程安全的 ? 是否存在潛在的並發問題?
=====更新===
根據答案還有兩個問題:
Q1。 我看到以下有關使用按需初始化idom的答案。 但是,是不是不夠的,如果我只使用synchronized
關鍵字上getInstance()
靜態方法?
public static synchronized MyService getInstance() {
...
}
難道還使單例實例的創建原子化了嗎?
Q2。 如果僅通過MyService
實例使用MyFileAccessor
,是否仍需要使MyFileAccessor
為單例或在MyFileAccessor.class上同步? 我的意思是MyService是單例,不是已經保證只有一個實例能夠調用MyFileAccessor
方法嗎?
您目前實際上沒有單例課程。 因為您檢查instance == null
,然后可能非原子地分配instance = new MyService()
,所以兩個線程有可能會創建MyService
實例。
創建線程安全的單例的一種方法是使用單元素枚舉:
enum MyService {
INSTANCE;
// Rest of class body.
}
現在,您可以通過MyService.INSTANCE
獲取實例。
另一種選擇是按需初始化慣用語 ,它利用了一個類,直到第一次需要它才被初始化的事實:
class MyService {
private static class Holder {
private static final MyService INSTANCE = new MyService();
}
static MyService getInstance() {
return Holder.INSTANCE;
}
}
就像@Kayaman在下面指出的那樣,單例枚舉模式是當前首選的實現方式。 我可以考慮為什么要使用IOD習慣用法的原因,例如,如果需要擴展另一個類(枚舉不能擴展類,因為它們已經擴展了Enum
;但是,它們可以實現接口)。
為了完整起見 ,另一個模式是Double-checked Locking ,如果發現instance
為null
,它將使用同步塊:
static MyService getInstance() {
if (instance == null) {
synchronized (MyService.class) {
if (instance == null) {
instance = new MyService();
}
}
}
return instance;
}
已經這樣做了,你也應該讓各個領域的final
:有一個之前發生保證最終場的分配相對於構造函數完成執行。
private final MyFileAccessor fileAccessor; // In MyService.
private final File mFile; // In MyFileAccessor.
否則,不能保證這些字段的值對所有線程都是可見的。
您還應該使MyFileAccessor
成為單例(例如,使用惰性持有人慣用語),或者使方法在MyFileAccessor.class
同步,以確保只有一個實例能夠同時調用這些方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.