Java 同步静态方法在android中不起作用?
我有一个班级需要单身:Java 同步静态方法在android中不起作用?,java,android,thread-safety,synchronized,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(
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
}
}
现在,由于这个方法是同步的,而且是静态的,所以应该一次只调用一次。当我在我的设备(安卓4.4)中运行它时,没有问题(在我的日志中,我得到了“set instance-instance set”),但是当我第一次在安卓2.2虚拟设备中运行它时(并且只有在安装后的第一次),或者每次在安卓4.4虚拟设备中运行它时,我得到了我的日志“set instance-set instance-instance set-instance set-instance set”,以及由此引发的一场车祸
因此,当启动“缓慢”时,它看起来会崩溃。在我看来,synchronized关键字似乎不起作用
此方法在应用程序的一开始就从两个不同的线程调用。一个线程从internet下载并解析文件,因此它会得到更新,而另一个线程(主线程)只是读取它。(我不需要一个先发生另一个,因为如果它不是从internet加载的,它只是从内存中读取)
这是来自虚拟设备的bug吗?我的意思是,因为它是同步的(据我所知),所以不能同时调用两次。我怎样才能解决这个问题
编辑:折射我的类并使其遵循单例模式神奇地修复了它。在我有访问getInstance的公共静态方法之前,“我不必把getInstance放在我想调用这个类的任何地方”。尽管如此,我还是要回滚代码以找出问题所在(我使用的是git回购)
Edit2:使用hashcode,我得到日志:“set instance 1139286928-set instance 1139286928-instance set 1139224312-instance set 1139287568”
Edit3:找到了bug。问题是loadDefaults方法在Resources文件夹中加载文件时,再次使用getInstance()解析该文件(调用我使用的loadFromString方法)。我以为它是两个不同的线程,但它是同一个线程,因此为什么同步对它没有任何影响。试试这个
// 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
}
您可能会得到不同之处…因此,正如我在编辑中所说的,问题是因为我从同一个方法调用了同步方法。这听起来可能像是递归,但在本例中不是,因为有一个可选参数会产生其他效果 如果得到相同的结果,只需在synchronized方法中打印/记录,以检查它是否跟踪到对同一方法的另一个调用
不要像我一样在代码的其余部分使用静态方法来避免getInstance() 只需将实例变量设置为final即可解决问题:
private static final StationsFile instance;
“从Java 6中,‘Final’变量具有特殊的线程安全语义,因为其他线程保证至少看到Final字段在其构造函数完成时的状态。”没有人可以阻止调度程序切换第一个线程的上下文,并让它在getInstance()之前运行第二个线程已完成执行。您确定没有因为某种原因涉及两个不同的类加载器吗?在这种情况下,您将有两个独立的类对象。在这一点上记录一些东西是值得的——例如,`Log.d(“StationsFile.class散列:”+StationsFile.class.hashCode())@JonSkeet真的很有趣,但事实并非如此。我对此进行了测试,在两次调用(1139286840)中都得到了相同的哈希代码。鉴于这不是您的实际代码(您已经说过您的实际代码有一个参数),您能否发布一些显示此问题的代码?在我看来,同步真的不起作用似乎不大可能。另外,您可以尝试在方法中使用
synchronized(StationsFile.class){…}
,这应该与您获得的代码相同,但至少检查起来很有趣。非常感谢。我发现了窃听器,是我的错。在第三次编辑中解释。您也可以非常友好地解释difference@blackbelt补充了一些解释。非常感谢你告诉我这个答案是否是近似的…非常好的答案。它修复了崩溃,但这不是我想要的100%。在我的例子中,我将为构造函数中的可选参数向getInstance传递一个参数(是的,我知道,非常脏)。对于这个解决方案,我要做的是getInstance().setParameter(param),这不是理想的解决方案。