Android SharedPreferences apply()方法是否存在竞态条件?
没错,apply()确实容易受到竞态条件的影响,这得从它的异步提交机制和SharedPreferences的设计细节说起:
内存即时更新与异步磁盘写入的顺序差
apply()会立刻把变更同步到内存中的SharedPreferences对象,但磁盘写入任务会被放到一个后台单线程队列中依次执行。如果多个线程连续调用apply()修改同一个或不同的key,内存里的状态会是最新的,但磁盘上的状态会严格按照任务提交顺序更新。如果在磁盘写入完成前进程意外终止,队列中未执行的apply()任务会丢失,导致磁盘最终的持久化状态和内存中的最新状态不一致——这就是典型的竞态导致的数据丢失场景。跨线程操作缺乏强原子性保障
虽然官方文档提到SharedPreferences实例是线程安全的,但Editor对象并非如此。如果多个线程同时获取独立的Editor实例修改不同的key,随后调用apply(),内存中的更新是即时生效的,但磁盘写入是多个独立的异步任务。如果在这些任务执行过程中进程崩溃,可能部分变更被写入磁盘,部分丢失,最终出现磁盘状态和内存状态不匹配的情况。无回调导致的依赖逻辑失效
和commit()不同,apply()没有返回值,也不会通知磁盘写入是否成功。如果你的业务逻辑依赖于磁盘写入完成后的状态(比如后续读取磁盘文件做校验),在apply()调用后立刻执行这类逻辑,就可能读取到旧的磁盘数据,和内存中的最新值产生冲突,这也是一种竞态条件的表现。
如果要规避这类风险,你可以根据场景选择方案:
- 若不担心主线程阻塞,使用
commit()同步提交,确保变更立刻持久化; - 对多线程修改的场景,自己添加同步锁(比如针对SharedPreferences实例加锁),保证修改操作的原子性;
- 对数据一致性要求高的场景,考虑使用更可靠的线程安全持久化方案(比如Room数据库)。
内容的提问来源于stack exchange,提问作者Shawn Li
相关产品推荐
相关产品推荐

