同步静态方法在android中不起作用?

时间:2014-02-24 10:22:02

标签: java android thread-safety synchronized

我有一个需要单身的课程:

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方法)。我认为它是两个不同的线程,但它是相同的,因此同步对它没有任何影响。

3 个答案:

答案 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'最终'变量具有特殊的线程安全语义,因为其他线程保证至少在其构造函数完成时看到最终字段。”