最近开发中,需要再多线程中操作数据库,但是Android的sqlite数据库是不能多线程写读写的。
先看一下报的错误:
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5): , while compiling: PRAGMA journal_mode
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
看一下代码
public class DBTestActivity extends BaseActivity {
private Button mAddStudent_bt;
@Override
protected void initView() {
setContentView(R.layout.ac_dbtest);
mAddStudent_bt=(Button)findViewById(R.id.ac_add_student_bt);
mAddStudent_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addStudent();
}
});
}
private void addStudent() {
//模拟1000个线程 加入数据库
for(int i=0;i<1000;i++){
new Thread(){
@Override
public void run() {
super.run();
try {
//随机休眠3秒以内的时间
Thread.sleep((long) (Math.random( )*1000*3));
} catch (InterruptedException e) {
e.printStackTrace();
}
Student s=new Student();
s.setStudentAge("1");
s.setStudentName("name:"+ new Date().getTime());
StudentDao.getInstance().addStudent(s);
}
}.start();
}
}
}
public class StudentDao {
private static volatile StudentDao dao;
public static final String TAG="StudentDao";
private StudentDao(){
}
public static StudentDao getInstance(){
if(dao==null){
synchronized (StudentDao.class){
if(dao==null){
dao=new StudentDao();
}
}
}
return dao;
}
//添加学生的方法,会在多个线程中调用
public void addStudent(Student student){
ContentValues contentValues=new ContentValues();
contentValues.put("s_id", UUID.randomUUID().toString());
contentValues.put("s_name", student.getStudentName());
contentValues.put("s_age",student.getStudentAge());
DbManager dBManager=new DbManager(BaseApp.getBaseApplicationContext());
SQLiteDatabase writableDatabase = dBManager.getWritableDatabase();
long result= writableDatabase.insertOrThrow("student",null,contentValues);//这里返回行号
Log.e(TAG, "执行的结果" +result+" "+Thread.currentThread().getName());
writableDatabase.close();
}
}
为了解决这个问题,我把所有的写操作放在一个线程里,保证每次调用写操作都只有一个线程,那么所有的 写方法都必须枷锁。
另外翻了一下资料,发现这个sqlite 的锁是库级别的,所以当有多个线程的时候就会涉及到同步问题。
我在dao层写了一个单线程的线程池,所有的写的操作的方法在这个线程池里调用,就ok了。
代码如下
public class StudentDao {
private static volatile StudentDao dao;
private static ExecutorService singThread
public static final String TAG = "StudentDao";
private StudentDao() {
}
public static StudentDao getInstance() {
if (dao == null) {
synchronized (StudentDao.class) {
if (dao == null) {
dao = new StudentDao();
singThread= Executors.newSingleThreadExecutor();
}
}
}
return dao;
}
public void addStudent(final Student student) {
Runnable runnable=new Runnable() {
@Override
public void run() {
ContentValues contentValues = new ContentValues();
contentValues.put("s_id", UUID.randomUUID().toString());
contentValues.put("s_name", student.getStudentName());
contentValues.put("s_age", student.getStudentAge());
DbManager dBManager = new DbManager(BaseApp.getBaseApplicationContext());
SQLiteDatabase writableDatabase = dBManager.getWritableDatabase();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long result = writableDatabase.insertOrThrow("student", null, contentValues);//这里返回行号
Log.e(TAG, "执行的结果1 " + result + " " + Thread.currentThread().getName());
writableDatabase.close();
}
};
singThread.execute(runnable);
}
}
简单测试了一下,还没有出现了报错的问题,可能是我的数据比较小吧,找个机会多用点数据再测试一下。这种做法只是想的到一个临时解决办法,并不能够做为方法放在项目里,如果有好的方案,可以留言告诉我一下,谢谢。