2014-02-24 37 views
1

我有一个类,它必须是一个单例:同步静态方法不能在android中工作?

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)中运行它时,没有任何问题(在我的日志中,我获得了“set instance - instance set”),但是当我在Android中第一次(并且仅在安装后第一次) 2.2虚拟设备或每次在Android 4.4虚拟设备中,我都会在我的日志“set instance - set instance - instance set - instance set”中出现,并因此导致崩溃。

所以它看起来像当启动“慢”崩溃。对我来说,它看起来像synchronized关键字不起作用。

,此方法被从2个不同的线程在应用程序的一开始叫。一个线程从互联网上下载并解析文件,所以它被更新,而另一个线程(主线程)只是读取它。 (我不需要一个在另一个之前发生,因为如果它没有从互联网加载它只是从内部存储器中读取它)。

这是来自虚拟设备的错误吗?我的意思是,因为它是同步的(并且据我所知),所以不能同时调用两次。我怎样才能解决这个问题?

编辑:refractoring我的课,并使其遵循Singleton模式神奇地修复它。在我有公共静态方法访问getInstance之前,所以“我不必把getInstance放到任何地方我都想调用这个类”。尽管如此,我要回滚我的代码以找出问题所在(我正在使用git回购)。

EDIT2:使用哈希码,我得到的日志: “设置实例1139286928 - 设置实例1139286928 - 例如设置1139224312 - 例如设置1139287568”

EDIT3:发现的bug。问题是,当他在Resourcers文件夹中加载文件时,loadDefaults方法,他再次使用getInstance()分析该文件(调用我所拥有的loadFromString方法)。我认为这是两个不同的主题,但它是同一个主题,因此为什么同步对它没有任何影响。

+0

没有人能阻止调度程序上下文切换你的第一个线程,并让运行第二个,之前的getInstance()完成之后才能执行。 – Blackbelt

+4

你确定你没有参与某种原因两个不同的类加载器?在这种情况下, ,你就会有两个独立的Class对象这将是值得记录身边的东西 - 比如'Log.d(“StationsFile.class哈希:” + StationsFile.class.hashCode()); –

+0

@JonSkeet真的野趣,但它的不是这样的。我测试了一下,我得到了在这两个调用相同的hashCode(1139286840)。 – olivarra1

回答

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 
} 

您可能会收到差异是什么...

+0

你也可以这么好解释有什么区别 – Blackbelt

+0

@blackbelt补充了一些解释。真的很感激,如果你指出我是否这个答案是近似的或不... –

+0

非常好的答案。它修复了崩溃,但它不是我想要的100%。在我的情况下,我传递一个参数给getInstance(是的,非常脏,我知道)在构造函数中的可选参数。通过这个解决方案,我会去做getInstance()。setParameter(param),这不是理想的解决方案。 – olivarra1

0

所以,我的编辑说,这个问题是因为我打电话从同步方法同样的方法。这可能听起来像一个递归,但在这种情况下,它不是,因为有一个可选参数可以产生其他效果。

如果你得到一样的,只是打印/记录您stack trace的同步方法来检查阉的痕迹,它再次调用同样的方法。

,并且不使用静态方法来避免的getInstance()在像我一样的代码的休息!

0

只是让你的实例变量最终会解决这个问题:

private static final StationsFile instance; 

“从Java 6‘最终’变量具有特殊的线程安全的语义,在其他线程保证看到的最后一个字段至少是它的构造函数完成时的状态。“