[英]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.