sqlitedatabase is locked
·

在操作Orm数据库sqlite时, 当存在多线程/多进程并行写时报错。 支持同一时刻多个读, 但同一时刻只能有一个写(不能有读); 简单记: 读写、写写是互斥的, 读读是可以并发的。
因为多线程、多进程可以通过数据库名称操作同一个数据库, 建议只在一个进程中实例单例访问同一个数据库, 从而实现互斥判断逻辑;
什么是写? 增加insert、删除delete、更新update和数据库表结构变化(例如修改已存在表的字段)
什么是读? 只有一个query
数据库存放在手机的 /data/data/包名/databases 中, 例如贝壳在 /data/data/com.lianjia.beike/databases 。
推荐使用try/catch/finally实现资源访问、回收;
使用事务时必须保证beginTransaction和endTransaction函数成对执行!!!
要点:单进程、单例、读写锁、数据库名称全局唯一
SqliteDatabase的close方法使得引用计数减1, 当引用计数为0时释放资源, 可根据业务需要评估是否执行。
推荐数据库写法:
class ***Helper extends SQLiteOpenHelper {
private static final String DB_NAME = "***.db"; //数据名称, 一般由英文和下划线组成
private static final int DB_VERSION = 1;
//单例, 避免创建多个SQLiteOpenHelper实例
private static volatile ***Helper sHelper;
private final ReentrantReadWriteLock mReadWriteLock = new ReentrantReadWriteLock(); //读写锁支持读读并发、读写/写写互斥
private final Lock mReadLock = mReadWriteLock.readLock(); //读锁
private final Lock mWriteLock = mReadWriteLock.writeLock(); //写锁
public static ***Helper getInstance(@NonNull Context context) {
if (mDbHelper == null) {
synchronized (***Helper.class) {
if (sDbHelper == null) {
sDbHelper = new *Helper(context);
}
}
}
return mDbHelper;
}
private ***Helper(Context context) {
if (sHelper != null) {
throw new IllegalArgumentException("不支持反射实例化,整个进程只能有一个实例");
}
}
/**
* 使用写锁和getWritableDatabase, 也可以使用tryLock并设置超时时间, lock是一直等待
**/
public int delete/insert/update(***) {
...
SQLiteDatabase database = null;
try {
mWriteLock.lock(); //获取写锁
database = getWritableDatabase();
count = database.delete(***); //database.update , database.insert等
} catch (Throwable e){
}finally {
try {
mWriteLock.unlock(); //释放写锁
} catch (Exception e) {
e.printStackTrace();
}
if (database != null) {
//实际上是引用计数减1, 当引用计数为0时才执行真正的释放逻辑
database.close();
}
}
...
}
/**
* 使用读锁和getReadableDatabase(), 也可以改造为tryLock并设置超时时间, lock会一直等待
**/
public List query(***) {
Cursor cursor = null;
SQLiteDatabase database = null;
try {
mReadLock.lock();
database = getReadableDatabase();
cursor = database.query(...);
//遍历cursor填充数据
} catch (Throwable e) {
} finally {
if (cursor != null) {
cursor.close();
}
if (database != null) {
database.close(); //非必要操作, 引用计数减1. 引用计数为0时释放资源。
}
mReadLock.unlock();
}
return 数据;
}
/**
beginTransaction和endTransaction必须成对执行
* endTransaction函数注释:This method must be called exactly once for each call to {@link #beginTransaction}.
*/
public void query(***) {
Cursor cursor = null;
SQLiteDatabase database = null;
try {
mReadLock.lock();
database = getReadableDatabase();
db.beginTransaction();
cursor = database.query(...); //数据库查询操作
db.setTransactionSuccessful(); //更新状态
} catch (Throwable e) {
} finally {
if (cursor != null) {
cursor.close();
}
if (database != null) {
if (db.inTransaction()) { //当前有等待操作则进入函数体, 即如果执行beginTransaction函数成功则条件为真。 如果beginTransaction语句后面出现崩溃则条件为false
db.endTransaction(); //如果执行了beginTransaction, 则必须保证执行endTransaction.
}
database.close(); //非必要操作, 引用计数减1. 引用计数为0时释放资源。
}
mReadLock.unlock();
}
return 数据;
}
更多推荐

所有评论(0)