探究Android SQLite3多线程

2023-11-17

http://www.cnblogs.com/xyczero/p/4096297.html

最近做项目时在多线程读写数据库时抛出了异常,这自然是我对SQlite3有理解不到位的地方,所以事后仔细探究了一番。

  1. 关于getWriteableDataBase()和getReadableDatabase()的真正作用
    getWriteableDataBase()其实是相当于getReadableDatabase()的一个子方法,getWriteableDataBase()是只能返回一个以读写方式打开的SQLiteDatabase的引用,如果此时数据库不可写时就会抛出异常,比如数据库的磁盘空间满了的情况。而getReadableDatabase()一般默认是调用getWriteableDataBase()方法,如果数据库不可写时就会返回一个以只读方式打开的SQLiteDatabase的引用,这就是二者最明显的区别。
    关键源码如下:

    public synchronized SQLiteDatabase getWritableDatabase() {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
            // darn! the user closed the database by calling mDatabase.close()
            mDatabase = null;
            } else if (!mDatabase.isReadOnly()) {
            return mDatabase;  // The database is already open for business
            }
        }
    ... ...
    
    public synchronized SQLiteDatabase getReadableDatabase() {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
            // darn! the user closed the database by calling mDatabase.close()
            mDatabase = null;
            } else {
            return mDatabase;  // The database is already open for business
            }
        }
        ... ...
        try {
            return getWritableDatabase();
        }
    ... ...
  2. SQLiteDatabase的同步锁
    其实在只使用一个SQLiteDatabase的引用时,SQLiteDatabase对CRUD操作都会加上一个锁(因为是db文件,所以精确至数据库级),这就保证了在同一时间你只能进行一项操作,无论是不是在同一个线程中,这就导致了如果你在程序中对SQLiteOpenHelper使用了单例模式,那么你对数据库读写进行任何的优化操作都是"徒劳"。

  3. 多线程读数据库
    仔细看源码你会发现,在数据库操作中只有add,delete,update会调用lock(),而query()是不会调用的,但是在加载数据时,调用了SQLiteQuery的fillWindow方法,而该方法依然会调用SQLiteDatabase.lock(),所以要想真正的实现多线程读数据库,只能每个线程使用各自的SQLiteOpenHelper对象进行读操作,这样就可避开同步锁。关键源码如下:

    /* package */ int fillWindow(CursorWindow window,
        int maxRead, int lastPos) {
        long timeStart = SystemClock.uptimeMillis();
        mDatabase.lock();
        mDatabase.logTimeStat(mSql, timeStart, SQLiteDatabase.GET_LOCK_LOG_PREFIX);
        try {... ...
  4. 多线程读写
    实现多线程读写的关键是enableWriteAheadLogging属性,这个方法 API Level 11添加的,也就是所3.0以上的版本就基本不可能实现真正的多线程读写了。简单的说通过调用enableWriteAheadLogging()和disableWriteAheadLogging()可以控制该数据是否被运行多线程读写,如果允许,它将允许一个写线程与多个读线程同时在一个SQLiteDatabase上起作用。实现原理是写操作其实是在一个单独的log文件,读操作读的是原数据文件,是写操作开始之前的内容,从而互不影响。当写操作结束后读操作将察觉到新数据库的状态。当然这样做的弊端是将消耗更多的内存空间。

  5. 多线程写
    这个就不用多想了,SQLite压根不支持,如果实在有需求可以使用多个数据库文件。

  6. 备注
    (1)你有没有想SQLite最多支持多少个数据库连接,其实在官方API文档(enableWriteAheadLogging ()方法)中给出了最精确的答案:The maximum number of connections used to execute queries in parallel is dependent upon the device memory and possibly other properties.就是看你有多少内存,但是我感觉这话说的有点大,是不?哈哈。

    (2)当你在多线程中只使用一个SQLiteDatabase的引用时,需要格外注意你SQLiteDataBase.close()调用的时机,因为你是使用的同一个引用,比如在一个线程中当一个Add操作结束后立刻关闭了数据库连接,而另一个现场中正准备执行查询操作,但此时db已经被关闭了,然后就会报异常错误。此时一般有三种解决方案,①简单粗暴给所有的CRUD添加一个 synchronized关键字;②永远不关闭数据库连接,只在最后退出是关闭连接。其实每次执行getWriteableDataBase()或getReadableDatabase()方法时,如果有已经建立的数据库连接则直接返回(例外:如果旧的连接是以只读方式打开的,则会在新建连接成功的前提下,关闭旧连接),所以程序中将始终保持有且只有一个数据库连接(前提是单例),资源消耗的很少。③可以自己进行引用计数,简单示例代码如下:

    //打开数据库方法
    public synchronized SQLiteDatabase openDatabase() {
    if (mOpenCounter.incrementAndGet() == 1) {
        // Opening new database
        try {
            mDatabase = sInstance.getWritableDatabase();
        } catch (Exception e) {
            mDatabase = sInstance.getReadableDatabase();
            }
        }
    return mDatabase;
    }
    
    //关闭数据库方法
    public synchronized void closeDatabase() {
        if (mOpenCounter.decrementAndGet() == 0) {
        // Closing database
        mDatabase.close();
        }
     }

    (3)还有一些比较好的习惯和常识,例如关闭Cursor,使用Transaction,SQLite存储数据时其实不区分类型,以及SQLite支持大部分标准SQL语句,增删改查语句都是通用的等等。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

探究Android SQLite3多线程 的相关文章

随机推荐

  • 计算机开机时间不对,为什么电脑每次开机时间都不对

    电脑是很智能的 一般情况下一开机电脑上面的时间就会自动校准 可有时候也会出现毛病 比如每次开机时间都不对 那么这是为什么呢 windows电脑 电脑时间不准的原因有 1 主板电池没电 2 有病毒 3 主板出问题 对应的解决方法 1 更换主板
  • 2023华为OD机试真题Java实现【寻找最大价值的矿堆/深度优先搜索】

    前言 本题使用Java实现 如果需要Python代码 请点击以下链接 点我 题目 我们规定 0表示空地 1表示银矿 2表示金矿 矿堆表示由相邻的金矿或银矿连接形成的地图 银矿价值是1 金矿价值是2 你的目标是找出地图中最大价值的矿堆 并且输
  • react页面渲染之前_React 单页面初始化渲染优化

    由于项目使用 React React Router Redux 来开发项目 采取了前端渲染的方式 如果采用 ssr 就不会有这个问题 问题 所以每到一个页面 需要发一个初始化的请求来获取页面数据 然后重新渲染页面 所以之前一个页面从请求到加
  • PP-YOLOE的译读笔记

    PP YOLOE An evolved version of YOLO 摘要 本文介绍了PP YOLOE模型 一种SOTA的产业级目标检测器 具有性能高效和部署友好的特点 本文在之前的PP YOLOv2的基础上进行优化 使用了anchor
  • linux如何脚本监控tps,Linux性能监控分析命令(三)—iostat命令介绍

    性能监控分析的命令包括如下 1 vmstat 2 sar 3 iostat 4 top 5 free 6 uptime 7 netstat 8 ps 9 strace 10 lsof 命令介绍 iostat是对系统的磁盘I O操作进行监控
  • vs中nuget包引用感叹号解决

    移除其中一个包引用 然后重新再nuget中搜索 然后在添加进来 其他的有感叹号的nuget包引用也自动刷新包了 就解决了
  • +-字符串(简单贪心)

    字符串 时间限制 1000 ms 内存限制 65535 KB 难度 1 描述 Shiva得到了两个只有加号和减号的字符串 字串长度相同 Shiva一次可以把一个加号和它相邻的减号交换 他想知道最少需要多少次操作才能把第一个字符串变换成第二个
  • python 安装第三方库imblearn

    首先把自己的numpy scikit learn scipy卸载掉 然后执行 pip install imblearn i http pypi douban com simple trusted host pypi douban com 如
  • MySQL学习笔记

    文章目录 一 数据库概述及数据准备 SQL DB DBMS 表 table SQL语句分类 导入数据 查看表结构 查看表中的数据 二 常用命令 查看MySQL版本 创建数据库 查询当前使用的数据库
  • ChatGPT/InstructGPT详解

    前言 GPT系列是OpenAI的一系列预训练文章 GPT的全称是Generative Pre Trained Transformer 顾名思义 GPT的目的就是通过Transformer为基础模型 使用预训练技术得到通用的文本模型 目前已经
  • sublime主题配色

    ignored packages Vintage theme Default Dark sublime theme dark theme Default sublime theme light theme Adaptive sublime
  • 【华为OD机试】生日礼物【2023 B卷

    华为OD机试 真题 点这里 华为OD机试 真题考点分类 点这里 题目描述 小牛的孩子生日快要到了 他打算给孩子买蛋糕和小礼物 蛋糕和小礼物各买一个 他的预算不超过x元 蛋糕cake和小礼物gift都有多种价位的可供选择 请返回小牛共有多少种
  • 【vue3】watchEffect只监听一次的方法

    import watchEffect from vue let data ref watchEffect gt console log data value 要利用data value执行的操作 而且还必须在watchEffect里面执行
  • 记录错误:con not perform the following tasks

    Ubuntu系统下安装软件出现报错 con not perform the following tasks TOC 检查软件源 实在不行改为国内源 或者等待网络再次下载
  • Bootstarp4 设计网页轮播组件

    很多网站都有广告轮播功能 可使用bootstrap4中的carousel组件非常简单的实现 目录 下载bootstrap4 轮播功能实现 简单实现轮播组件 增加标识图标 增加标题和说明 切换淡入淡出 设置数据间隔 总结 下载bootstra
  • 基于微信云开发实现电影推荐小程序

    一 项目背景 项目名称为柚子电影 此小程序的目的是为了给大家推荐电影 与其他的售票等小程序不同 二 性能需求 我的影单的增加 删除和查询 电影详情页面的完整实现 对小程序的各个方面 电影推荐 电影详情 用户授权 影院查询 影院位置 用户登录
  • Scala(一):概述&变量&流程控制(转载)

    文章目录 一 简介 1 1 scala语言的特点 1 2 第一个scala程序 二 变量 2 1 Scala变量的使用 2 2 Scala数据类型 2 3 值类型转换 三 循环控制 3 1 分支控制if else 3 2 for循环控制 S
  • opencv4应用开发基础

    opencv3 0版本以上都是用C 实现的 常用的一些函数及类型集中在cv命名空间里 cv Mat类型用于表示一个图像 构造函数除了空的构造函数外 还有很多个 Mat int rows int cols int type 创建指定行 列数的
  • springcloud常见面试题(2023最新)

    目录 前言 一 微服务 1 微服务是什么 2 你知道哪些RPC框架 3 springCloud和Dubbo有什么区别 4 SpringCloud由什么组成 二 Spring Cloud Eureka 1 Eureka包含几个组件 2 Eur
  • 探究Android SQLite3多线程

    http www cnblogs com xyczero p 4096297 html 最近做项目时在多线程读写数据库时抛出了异常 这自然是我对SQlite3有理解不到位的地方 所以事后仔细探究了一番 关于getWriteableDataB