SharedPreferences 和线程安全
Posted
技术标签:
【中文标题】SharedPreferences 和线程安全【英文标题】:SharedPreferences and Thread Safety 【发布时间】:2011-06-09 06:24:42 【问题描述】:看着SharedPreferences docs 它说:
"注意:目前这个类没有 支持跨多个进程使用。 这将在稍后添加。”
所以它本身似乎不是线程安全的。但是,commit() 和 apply() 做了什么样的保证呢?
例如:
synchronized(uniqueIdLock)
uniqueId = sharedPreferences.getInt("UNIQUE_INCREMENTING_ID", 0);
uniqueId++;
sharedPreferences.edit().putInt("UNIQUE_INCREMENTING_ID", uniqueId).commit();
在这种情况下是否可以保证 uniqueId 始终是唯一的?
如果没有,是否有更好的方法来跟踪持续存在的应用程序的唯一 ID?
【问题讨论】:
示例代码同步到非最终字段并更改它。我怀疑你想这样做。uniqueIdLock
是什么?它在哪里定义?它是静态的,最终公开的等吗?另外,也许您应该切换接受的答案??
@Bloodboiler 在这种情况下会发生什么?
【参考方案1】:
进程和线程是不同的。 android 中的 SharedPreferences 实现是线程安全的,但不是进程安全的。通常,您的应用程序将在同一个进程中运行,但您可以在 AndroidManifest.xml 中对其进行配置,例如,服务运行在与活动不同的进程中。
要验证线程安全性,请参阅 AOSP 中的 ContextImpl.java 的 SharedPreferenceImpl。请注意,在您期望的任何地方都有同步。
private static final class SharedPreferencesImpl implements SharedPreferences
...
public String getString(String key, String defValue)
synchronized (this)
String v = (String)mMap.get(key);
return v != null ? v : defValue;
...
public final class EditorImpl implements Editor
public Editor putString(String key, String value)
synchronized (this)
mModified.put(key, value);
return this;
...
但是,对于您的唯一 id 情况,您似乎仍然需要同步,因为您不希望它在获取和放置之间发生变化。
【讨论】:
this
调用的synchronized(this)
部分是指外部对象还是内部对象?我在 google 群组上找到了说明锁在不同对象上的参考资料 - 不确定这是否正确。
@Richard:您无法从上面的 sn-p 中看出,但随便检查一下,代码是线程安全的。那里有两个不同的锁——一个在 SharedPreferencesImpl 的实例上,另一个在 EditorImpl 的实例上——但保护不同的东西:mMap 和 mModified。
@G.BlakeMeike:除了mMap
和mModified
最终被读取和写入同一个底层文件,访问似乎被锁定在不同的监视器上。不过我也只是随便看了一下代码,没有做压力测试。 (答案已更新)
尽管SharedPreferencesImpl
被认为是线程安全的并且每个文件使用一个进程唯一的单例,但重要的是要注意它的用法本质上不是原子的:当两个编辑器同时修改首选项时,根据SharedPreferences.Editor 文档,最后一个调用commit()
或apply()
获胜。
@AbdEl-RahmanEl-Tamawy 在那种情况下不会有问题,但我不会称之为'克服“最后一次提交获胜”的问题'。
【参考方案2】:
我也在想同样的事情 - 并且遇到了 this thread 说它们不是线程安全的:
Context.getSharedPreferences() 和 Editor.commit 的实现 () 不要在同一台显示器上同步。
此后我查看了要检查的 Android 14 代码,其中涉及的内容非常多。特别是SharedPreferencesImpl
在读写磁盘时似乎使用了不同的锁:
enqueueDiskWrite()
锁定 mWritingToDiskLock
startLoadFromDisk()
锁定 this
,并在 SharedPreferencesImpl.this
上启动线程锁定
我不相信这段代码真的是安全的。
【讨论】:
引用是正确的:不要在同一台显示器上同步。另一方面,从随意阅读代码中,我看不出他们为什么应该这样做。同样,只是快速阅读,但在我注意到他们访问共享可变状态的唯一地方,do 使用相同的监视器。【参考方案3】:我认为这样就可以了。
您可以在同步部分中使用 sleep 对其进行测试,并从不同的线程调用它
【讨论】:
【参考方案4】:您应该知道 SharedPreferences 不适用于三星手机,请查看android issue。
我已经实现了简单的数据库首选项存储,您可以在 github 上找到它。
干杯,
【讨论】:
有谁知道现在是不是这样?或者大多数 Galaxy S 用户现在是否已经升级到一些修正了错误的版本?考虑到 Galaxy S 型号的受欢迎程度,我认为这是使用 SharedPreferences 的一大危险信号 - 对吗? 今天不要认为这是个问题 在那个 android 问题线程中它是 GINGERBREAD.XXJVK 中的 reported to be fixed以上是关于SharedPreferences 和线程安全的主要内容,如果未能解决你的问题,请参考以下文章
Android开发笔记(一百七十八)更安全的数据仓库DataStore
Android开发笔记(一百七十八)更安全的数据仓库DataStore
Android开发笔记(一百七十八)更安全的数据仓库DataStore