我有一个需要单身的课程:
private static StationsFile instance;
private Context ctx;
protected StationsFile(Context ctx){
this.ctx = ctx;
load();
}
public static synchronized StationsFile getInstance(Context ctx){
if(instance == null){
Log.d("StationsFile", "set instance " + StationsFile.class.hashCode());
instance = new StationsFile(ctx);
Log.d("StationsFile", "instance set " + instance.hashCode());
}
return instance;
}
protected static StationsFile getInstance(){
return getInstance(null);
}
private void load(){
if(externalStorageIsReadable()){
// Loads from sd file
}else{
loadDefaults();
}
}
private void loadDefaults(){
if(this.ctx != null){
// Load from raw file in raws folder (I need a context to access Resources)
}else{
// Hardcoded default here
}
}
现在,由于此方法是同步的并且它是静态的,因此它应该一次只调用一次。当我在我的设备(Android 4.4)中运行它时,没有问题(在我的日志中我得到“设置实例 - 实例设置”),但是当我在Android中第一次(并且仅在安装后第一次)运行它时2.2虚拟设备或每次在Android 4.4虚拟设备中,我都会在我的日志中“设置实例 - 设置实例 - 实例集 - 实例集”,并因此而崩溃。
所以看起来当开始“慢”时它会崩溃。对我而言,似乎synchronized关键字不起作用。
此方法在应用程序的最开始从2个不同的线程调用。一个线程从Internet下载并解析该文件,因此它会更新,而另一个线程(主线程)只是读取它。 (我不需要在另一个之前发生一个,因为如果它没有从互联网加载它只是从内部存储器中读取它。)
这是虚拟设备的错误吗?我的意思是,因为它是同步的(据我所知),这不能同时被调用两次。我该如何解决这个问题?
编辑:对我的课进行折射并使其遵循单身模式神奇地修复它。在我使用访问getInstance的公共静态方法之前,“我不必将getInstance放在任何地方,我想调用这个类”。尽管如此,我将回滚我的代码以找出问题所在(我正在使用git repo)。
Edit2:使用哈希码,我得到日志:“set instance 1139286928 - set instance 1139286928 - instance set 1139224312 - instance set 1139287568”
Edit3:发现错误。问题是方法loadDefaults在他将文件加载到Resourcers文件夹时,他再次使用getInstance()解析该文件(调用我有的loadFromString方法)。我认为它是两个不同的线程,但它是相同的,因此同步对它没有任何影响。
答案 0 :(得分:1)
试试这个......
// initialized when the class is loaded for the first time
private static final StationsFile instance = new StationFile();
public static StationsFile getInstance() {
return instance; // no need of null check here. No worry about synchronization
}
你可能会得到差异......
答案 1 :(得分:0)
所以,正如我在编辑中所说,问题是因为我从同一个方法调用了synchronized方法。它可能听起来像一个递归,但在这种情况下它不是,因为有一个可选参数产生其他效果。
如果你得到同样的结果,只需在同步方法中打印/记录你的stack trace,检查它是否跟踪到同一方法的另一个调用。
并且不要像我一样使用静态方法来避免其余代码中的getInstance()!
答案 2 :(得分:0)
只需将您的实例变量设为final即可解决问题:
private static final StationsFile instance;
“从Java 6'最终'变量具有特殊的线程安全语义,因为其他线程保证至少在其构造函数完成时看到最终字段。”