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:除了mMapmModified 最终被读取和写入同一个底层文件,访问似乎被锁定在不同的监视器上。不过我也只是随便看了一下代码,没有做压力测试。 (答案已更新) 尽管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

Android开发笔记(一百七十八)更安全的数据仓库DataStore

Java线程安全和非线程安全

线程安全和非线程安全