Android多线程下操作sqlite数据库解决方案
问题:Android中的SQLite数据库并发访问
attempt to re-open an already-closed object
因为我们只使用一个数据库连接,Thread1和Thread2的都是由getDatabase()方法返回的相同连接。发生的什么事呢,在Thread2还在使用数据库连接时,Thread1可能已经把它给关闭了,那就是为什么你会得到崩溃异常。
我们需要确保在没有任何一个人在使用数据库时,才去关闭它。在StackOverflow上推荐的做法是永远不要关闭数据库。Android会尊重你这种做法,但会给你如下的提示。所以我一点也不推荐这种做法。
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
每次都创建了一个新的SQLiteOpenHelper,实际上你每次都创建了一个数据库的连接。如果你在同一时间用不同的数据库连接来对同一的数据库进行写操作的话,那么其中一个会失败。
解决方案:AtomicInteger
具体模拟代码(Android代码类似):
DataBaseManager
package com.gradle.java.thread;
import java.util.concurrent.atomic.AtomicInteger;
import com.gradle.android.utils.OkhttpUtils;
/**
* @author Arison
*管理sqllite数据库
*/
public class DataBaseManager {
private static DataBaseManager instance;
/**
*保证多线程下原子操作
*/
privateAtomicInteger i=new AtomicInteger();
public static DataBaseManager getInstance(){
if(instance==null){
synchronized (DataBaseManager.class) {
if (instance==null) {
OkhttpUtils.println("数据库管理类DataBaseManager--->单例初始化!");
instance=new DataBaseManager();
}
}
}
return instance;
}
/**
*模拟Android 数据库在多线程下的并发问题
* @param args
*/
public static void main(String[] args) {
for(int i=1;
i<=2000;
i++){
new Thread(new Runnable() {
@Override
public void run() {
//切记,打开,关闭数据库的操作不能直接SQLiteDatabase.getInstance().openDB();
SQLiteDatabase.getInstance().closeDB();
//必须调用管理者单例类DataBaseManager 来调用打开和关闭操作,从而解决多线程下访问sqlite数据库的问题
DataBaseManager.getInstance().openDataBase();
DataBaseManager.getInstance().closeDataBase();
}
},""+i).start();
}
}
public synchronized AtomicInteger openDataBase(){
OkhttpUtils.println("+++++++++++++++++++++++++++++++++++++++++++");
OkhttpUtils.println("线程"+Thread.currentThread().getName()+"--->open开始---->当前数据库连接数:"+i);
if (i.incrementAndGet()==1) {
OkhttpUtils.println("线程"+Thread.currentThread().getName()+"打开数据库之前!数据库状态:"
+SQLiteDatabase.getInstance().getStateDB());
SQLiteDatabase.getInstance().openDB();
//单例类模拟数据库打开操作
OkhttpUtils.println("线程"+Thread.currentThread().getName()+"执行数据库打开操作!");
OkhttpUtils.println("线程"+Thread.currentThread().getName()+"打开数据库之后!数据库状态:"
+SQLiteDatabase.getInstance().getStateDB());
}else{
OkhttpUtils.println("线程"+Thread.currentThread().getName()+"--->open()操作无效!新增一条数据库连接!---> 数据库状态:"
+SQLiteDatabase.getInstance().getStateDB());
}
OkhttpUtils.println("线程"+Thread.currentThread().getName()+"--->open完毕---->当前数据库连接数:"+i);
OkhttpUtils.println("+++++++++++++++++++++++++++++++++++++++++++");
return i;
}
public synchronized AtomicInteger closeDataBase(){
OkhttpUtils.println("--------------------------------------------");
OkhttpUtils.println("线程"+Thread.currentThread().getName()+"--->close开始---->当前数据库连接数:"+i);
if (i.decrementAndGet()==0) {
OkhttpUtils.println("线程"+Thread.currentThread().getName()+"关闭数据库之前!数据库状态:"
+SQLiteDatabase.getInstance().getStateDB());
SQLiteDatabase.getInstance().closeDB();
//单例类模拟数据库关闭操作
OkhttpUtils.println("线程"+Thread.currentThread().getName()+"执行数据库关闭操作!");
OkhttpUtils.println("线程"+Thread.currentThread().getName()+"关闭数据库之后!数据库状态:"
+SQLiteDatabase.getInstance().getStateDB());
}else{
OkhttpUtils.println("线程"+Thread.currentThread().getName()+"--->close()操作无效!关闭一条数据库连接!--->数据库状态:"
+SQLiteDatabase.getInstance().getStateDB());
}
OkhttpUtils.println("线程"+Thread.currentThread().getName()+"--->close完毕---->当前数据库连接数:"+i);
OkhttpUtils.println("--------------------------------------------");
return i;
}
}
package com.gradle.java.thread;
/**
* @author Arison
*模拟Android sqlite数据库
*/
public class SQLiteDatabase {
private static SQLiteDatabase instance;
private boolean isOpen=false;
public static SQLiteDatabase getInstance(){
if(instance==null){
synchronized (SQLiteDatabase.class) {
if (instance==null) {
instance=new SQLiteDatabase();
}
}
}
return instance;
}
publicvoid openDB(){
this.isOpen=true;
}
publicvoidcloseDB(){
this.isOpen=false;
}
publicboolean getStateDB(){
return isOpen;
}
}
运行效果图(局部)
文章图片
文章图片
项目代码:
【Android多线程下操作sqlite数据库解决方案】github【Gradle-demo】
推荐阅读
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- android第三方框架(五)ButterKnife
- 爱就是希望你好好活着
- 昨夜小楼听风
- 知识
- 死结。
- 我从来不做坏事
- 烦恼和幸福
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- Android中的AES加密-下