简体   繁体   English

我的服务线程安全吗?

[英]Is my service thread safe?

I have a singleton class MyService , it has two functions to read & write data from/to file: 我有一个单例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 class has synchronized functions to read & write file: 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
    …
  } 
}

My question : Is the singleton MyService class thread-safe when my project reading & writing data from/to file through it? 我的问题:当我的项目通过它读写数据时,单例MyService类是否是线程安全的 Are there potential concurrency issues? 是否存在潜在的并发问题?

===== UPDATE === =====更新===

Two more questions based on the answers: 根据答案还有两个问题:

Q1. Q1。 I see the answers below about using Initialized-On-Demand idom. 我看到以下有关使用按需初始化idom的答案。 But isn't it enough if I just use the synchronized keyword on getInstance() static method? 但是,是不是不够的,如果我只使用synchronized关键字上getInstance() 静态方法?

public static synchronized MyService getInstance() {
 ...
}

Doesn't it also make the singleton instance creation atomic? 难道还使单例实例的创建原子化了吗?

Q2. Q2。 If I only use MyFileAccessor through MyService instance, is it still necessary to make MyFileAccessor a singleton or synchronized on MyFileAccessor.class ? 如果仅通过MyService实例使用MyFileAccessor ,是否仍需要使MyFileAccessor为单例或在MyFileAccessor.class上同步? I mean MyService is a singleton, isn't it already guaranteed only one instance is able to invoke methods in MyFileAccessor ? 我的意思是MyService是单例,不是已经保证只有一个实例能够调用MyFileAccessor方法吗?

You don't actually have a singleton class at the moment. 您目前实际上没有单例课程。 Because you check if instance == null and then maybe assign instance = new MyService() non-atomically, there is a chance that two threads will create instances of MyService . 因为您检查instance == null ,然后可能非原子地分配instance = new MyService() ,所以两个线程有​​可能会创建MyService实例。

One way to create a thread-safe singleton is to use a single-element enum: 创建线程安全的单例的一种方法是使用单元素枚举:

enum MyService {
  INSTANCE;

  // Rest of class body.
}

You can now get your instance via MyService.INSTANCE . 现在,您可以通过MyService.INSTANCE获取实例。

An alternative is the Initialization-on-demand idiom , which makes use of the fact that a class is not initialized until it is first required: 另一种选择是按需初始化惯用语 ,它利用了一个类,直到第一次需要它才被初始化的事实:

class MyService {
  private static class Holder {
    private static final MyService INSTANCE = new MyService();
  }

  static MyService getInstance() {
    return Holder.INSTANCE;
  }
}

As @Kayaman notes below, the singleton enum pattern is the currently-preferred way of doing it. 就像@Kayaman在下面指出的那样,单例枚举模式是当前首选的实现方式。 I can think of reasons why you might want to use the IOD idiom, for instance if you need to extend another class (enums can't extend classes, since they already extend Enum ; however, they can implement interfaces). 我可以考虑为什么要使用IOD习惯用法的原因,例如,如果需要扩展另一个类(枚举不能扩展类,因为它们已经扩展了Enum ;但是,它们可以实现接口)。

For completeness, yet another pattern is Double-checked Locking , which uses a synchronized block if instance is found to be null : 为了完整起见 ,另一个模式是Double-checked Locking ,如果发现instancenull ,它将使用同步块:

static MyService getInstance() {
  if (instance == null) {
    synchronized (MyService.class) {
      if (instance == null) {
        instance = new MyService();
      }
    }
  }
  return instance;
}

Having done that, you should also make all fields final : there is a happens-before guarantee on the assignment of final fields with respect to the constructor finishing execution. 已经这样做了,你也应该让各个领域的final :有一个之前发生保证最终场的分配相对于构造函数完成执行。

private final MyFileAccessor fileAccessor; // In MyService.

private final File mFile;  // In MyFileAccessor.

Otherwise, there is no guarantee that these fields' values will be visible to all threads. 否则,不能保证这些字段的值对所有线程都是可见的。


You should also make MyFileAccessor either a singleton (eg using the lazy holder idiom), or make the methods synchronized on MyFileAccessor.class , to ensure that only one instance is able to invoke those methods at the same time. 您还应该使MyFileAccessor成为单例(例如,使用惰性持有人惯用语),或者使方法在MyFileAccessor.class同步,以确保只有一个实例能够同时调用这些方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM