簡體   English   中英

我的服務線程安全嗎?

[英]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 ,如果發現instancenull ,它將使用同步塊:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM