最近做项目时在多线程读写数据库时抛出了异常,这自然是我对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语句,增删改查语句都是通用的等等。

查看原文:http://www.xyczero.com/blog/article/9/.

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

  1. Android网络多线程断点续传下载

    本示例介绍在Android平台下通过HTTP协议实现断点续传下载. 我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多 ...

  2. 教你如何在 Android 使用多线程下载文件

    # 教你如何在 Android 使用多线程下载文件 前言 在 Android 日常开发中,我们会经常遇到下载文件需求,这里我们也可以用系统自带的 api DownloadManager 来解决这个问题 ...

  3. Android进阶——多线程系列之Thread、Runnable、Callable、Future、FutureTask

    多线程一直是初学者最抵触的东西,如果你想进阶的话,那必须闯过这道难关,特别是多线程中Thread.Runnable.Callable.Future.FutureTask这几个类往往是初学者容易搞混的. ...

  4. android开发--多线程

    android中的几种多线程实现方式: 1)Activity.runOnUiThread(Runnable) 2)View.post(Runnable) ;View.postDelay(Runnabl ...

  5. Android handle 多线程练习

    Android handle <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android&quo ...

  6. Android SQLite3工具常用命令行总结

    Android SDK的tools目录下提供了一个sqlite3.exe工具,这是一个简单的sqlite数据库管理工具.开发者可以方便的使用其对sqlite数据库进行命令行的操作. 程序运行生成的*. ...

  7. (原创)android Sqlite多线程访问异常解决方案

    在开发Android的程序的时候sqlite数据库是经常用到的:在多线程访问数据库的时候会出现这样的异常:java.lang.IllegalStateException: Cannot perform ...

  8. 36.Android之多线程和handle更新UI学习

    android经常用到多线程更新UI,今天学习下. 首先布局比较简单: <?xml version="1.0" encoding="utf-8"?> ...

  9. Android之多线程断点下载

    本文主要包含多线程下载的一些简单demo,包括三部分 java实现 android实现 XUtils开源库实现 注意下载添加网络权限与SD卡读写权限 java实现多线程下载 public class ...

随机推荐

  1. Java---JUnita、注解与类加载器详解以及实例

    JUnit软件测试技术(工具) 在项目中建立专门用户测试的包结构. 在Junit中,通过@Test注解,可以运行一个方法. ★ Junit注解说明 使用了@Test注解应该满足以下条件: 1) 必须是 ...

  2. Java运行环境的配置(JDK和JRE)

    Jdk 表示java开发环境,包含开发环境和运行环境 Jre 表示java运行环境 JDK就是Java Development Kit.简单的说JDK是面向开发人员使用的SDK,它提供了Java的开发 ...

  3. DB2 创建数据库

    0.一些准备工作可能用到的命令 db2cmd --进入db2命令行 db2 list database directory --显示已有的数据库 db2 drop db pcore --删除一个数据库 ...

  4. Lucene简介

    1 lucene简介1.1 什么是lucene    Lucene是一个全文搜索框架,而不是应用产品.因此它并不像www.baidu.com 或者google Desktop那么拿来就能用,它只是提供 ...

  5. sql第一课笔记

    这是我看了imooc的视频教程之后重新写的笔记. 虽然之前也是学习过SQL Server数据库,但是也是忘记得差不多了.现在重新捡起来,安装一次数据库练习,使用的是mysql. 第一课是最简单的创建, ...

  6. Away3D 4.1.4 中实现骨骼绑定

    骨骼的绑定归根结底就是将目标骨骼的位置以及旋转数据,同步给要绑定的显示对象. 先来看BindingTag.as package away3d.entities {     import away3d. ...

  7. html特殊符号列表

    特殊符号 命名实体 十进制编码 特殊符号 命名实体 十进制编码 Α Α Α Β Β Β Γ Γ Γ Δ Δ Δ Ε Ε Ε Ζ Ζ Ζ Η Η Η Θ Θ Θ Ι Ι Ι Κ Κ Κ Λ Λ Λ Μ ...

  8. JAX-WS + Spring 开发webservice

    通过几天的时间研究了下使用jax-ws来开发webservice,看了网上的一些资料总结出jax-ws的开发大概分为两种. 以下项目使用的spring3.0,jar包可以到官网下载 第一种:使用独立的 ...

  9. 3DS MAX 导出FBX到Unity3D设置

  10. 多版本号并发控制(MVCC)在分布式系统中的应用

    QQ群:289150599 问题 近期项目中遇到了一个分布式系统的并发控制问题.该问题能够抽象为:某分布式系统由一个数据中心D和若干业务处理中心L1,L2 ... Ln组成:D本质上是一个key-va ...