最近開發(fā)中,需要再多線程中操作數(shù)據(jù)庫,但是Android的sqlite數(shù)據(jù)庫是不能多線程寫讀寫的。
先看一下報的錯誤:
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個線程 加入數(shù)據(jù)庫
for(int i=0;i<1000;i++){
new Thread(){
@Override
public void run() {
super.run();
try {
//隨機休眠3秒以內(nèi)的時間
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;
}
//添加學(xué)生的方法,會在多個線程中調(diào)用
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, "執(zhí)行的結(jié)果" +result+" "+Thread.currentThread().getName());
writableDatabase.close();
}
}
為了解決這個問題,我把所有的寫操作放在一個線程里,保證每次調(diào)用寫操作都只有一個線程,那么所有的 寫方法都必須枷鎖。
另外翻了一下資料,發(fā)現(xiàn)這個sqlite 的鎖是庫級別的,所以當(dāng)有多個線程的時候就會涉及到同步問題。
我在dao層寫了一個單線程的線程池,所有的寫的操作的方法在這個線程池里調(diào)用,就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, "執(zhí)行的結(jié)果1 " + result + " " + Thread.currentThread().getName());
writableDatabase.close();
}
};
singThread.execute(runnable);
}
}
簡單測試了一下,還沒有出現(xiàn)了報錯的問題,可能是我的數(shù)據(jù)比較小吧,找個機會多用點數(shù)據(jù)再測試一下。這種做法只是想的到一個臨時解決辦法,并不能夠做為方法放在項目里,如果有好的方案,可以留言告訴我一下,謝謝。