繁体   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