您的位置:首页 > 数据库

如何在多线程操作数据库--多线程串行

2015-11-21 17:38 337 查看

前提

很多时候我们都是在主线程下直接打开数据库并对数据库操作的,但如果遇到加密型的数据库,例如SQLCipher加密数据库,或者是对字段进行加密的数据库;你就不得不在线程里面操作数据库了!

解决

大家都知道,在多线程操作数据库的时候,如果在各自线程中都利用之前的方法:实例化一个SQLiteOpenHelper类,然后在调用其中的方法。后面的就会遇到android.database.sqlite.SQLiteException:
database is locked
这样的异常。原因当然就是多线程的时候,导致的关闭异常,例如A线程的一个查询操作,在打开了数据库后,被B线程close了,导致A线程报错。要解决一样的问题,安全一点的方法是利用单例的形式创建SQLiteOpenHelper类或者是实现是具体数据库增删改查的Dao类,并且对方法添加synchronizaed关键字。
关键代码:StuDao 包含了对数据库的操作语句,在方法上都加上了锁
package com.lingdududu.testSQLiteDao;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.util.Log;

import com.lingdududu.testSQLiteDb.StuDBHelper;

public class StuDao {

// 创建StuDBHelper对象
StuDBHelper dbHelper = null;
// 得到一个可读的SQLiteDatabase对象
SQLiteDatabase db = null;
Cursor cursor = null;
static StuDao stuDao = null;

private String TABLE = "stu_table";
private String TAG = "StuDao";

static Object lock = new Object();

public static StuDao getInstance(Context context) {
if (stuDao == null) {
synchronized (lock) {
if (stuDao == null) {
stuDao = new StuDao(context);
}
}
}
return stuDao;
}

public void destoryDB() {
// 关闭数据库
close();
stuDao = null;
}

private StuDao(Context context) {
Log.e(TAG, "--->>>   StuDao");
dbHelper = new StuDBHelper(context, "stu_db", null, 1);
if (Build.VERSION.SDK_INT >= 11) {
dbHelper.getWritableDatabase().enableWriteAheadLogging();
}
}

Object queryLock = new Object();

public synchronized void queryTable() {
// 得到一个可写的数据库
db = dbHelper.getWritableDatabase();
// 参数1:表名
// 参数2:要想显示的列
// 参数3:where子句
// 参数4:where子句对应的条件值
// 参数5:分组方式
// 参数6:having条件
// 参数7:排序方式
cursor = db.query(TABLE,
new String[] { "id", "sname", "sage", "ssex" }, null, null,
null, null, null);
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex("sname"));
String age = cursor.getString(cursor.getColumnIndex("sage"));
String sex = cursor.getString(cursor.getColumnIndex("ssex"));
int id = cursor.getInt(cursor.getColumnIndex("id"));
System.out.println("query------->" + "id:" + id + " " + "姓名:"
+ name + " " + "年龄:" + age + " " + "性别:" + sex);
}
// 关闭数据库
this.close();

}

public synchronized void queryTable(int id) {
// 得到一个可写的数据库
db = dbHelper.getWritableDatabase();
// 参数1:表名
// 参数2:要想显示的列
// 参数3:where子句
// 参数4:where子句对应的条件值
// 参数5:分组方式
// 参数6:having条件
// 参数7:排序方式
cursor = db.query(TABLE,
new String[] { "id", "sname", "sage", "ssex" }, "id=?",
new String[] { Integer.toString(id) }, null, null, null);
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex("sname"));
String age = cursor.getString(cursor.getColumnIndex("sage"));
String sex = cursor.getString(cursor.getColumnIndex("ssex"));
int sid = cursor.getInt(cursor.getColumnIndex("id"));
Log.d(TAG, "id:" + sid + " " + "姓名:" + name + " " + "年龄:" + age
+ " " + "性别:" + sex);
}
// 关闭数据库
this.close();
}

public synchronized void updateTable(String id, String sage) {
// 得到一个可写的数据库
db = dbHelper.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put("sage", sage);
cv.put("id", id);
// where 子句 "?"是占位符号,对应后面的"1",
String whereClause = "id=?";
String[] whereArgs = { String.valueOf(id) };
// 参数1 是要更新的表名
// 参数2 是一个ContentValeus对象
// 参数3 是where子句
db.update(TABLE, cv, whereClause, whereArgs);
// 关闭数据库
this.close();
}

public synchronized void insertTable(String id, String sage) {
// 得到一个可写的数据库
db = dbHelper.getWritableDatabase();
// 生成ContentValues对象 //key:列名,value:想插入的值
ContentValues cv = new ContentValues();
// 往ContentValues对象存放数据,键-值对模式
cv.put("id", id);
cv.put("sname", "xiaoming");
cv.put("sage", sage);
cv.put("ssex", "male");
// 调用insert方法,将数据插入数据库
db.insert("stu_table", null, cv);
// 关闭数据库
// 关闭数据库
this.close();
}

public synchronized void deleteTable(int id) {
// 得到一个可写的数据库
db = dbHelper.getWritableDatabase();
String whereClauses = "id=?";
String[] whereArgs = { String.valueOf(id) };
// 调用delete方法,删除数据
db.delete(TABLE, whereClauses, whereArgs);
// 关闭数据库
this.close();
}

public synchronized void deleteTable() {
// db = dbHelper.getWritableDatabase();
// String sql = "drop table if exists "+TABLE;
// db.execSQL(sql);

// 得到一个可写的数据库
db = dbHelper.getWritableDatabase();
// 调用delete方法,删除数据
db.delete(TABLE, null, null);

// 关闭数据库
this.close();
}

public void close() {
if (db != null) {
db.close();
}
if (cursor != null) {
cursor.close();
}
}

}


在SQLiteActivityMulThread开启线程操作数据库:
package com.lingdududu.testSQLite;

import com.lingdududu.MyThreadPoolExecutor;
import com.lingdududu.testSQLiteDao.StuDao;
import com.lingdududu.testSQLiteDb.StuDBHelper;

import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

/*
* @author lingdududu
*/
public class SQLiteActivityMulThread extends Activity {
/** Called when the activity is first created. */
// 声明各个按钮
private Button insertDatabase;
private Button updateDatabase1;
private Button updateDatabase2;
private Button deleteDatabase;

int age;
int id = 100;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_mul);

// 调用creatView方法
creatView();
setListener();
// MyThreadPoolExecutor.getInstance().execute(new Runnable() {
//
// @Override
// public void run() {
// StuDao.getInstance(SQLiteActivityMulThread.this).queryTable();
// }
// });
// for (age = 0; age < 10; age++) {
// MyThreadPoolExecutor.getInstance().execute(new Runnable() {
// @Override
// public void run() {
// StuDao.getInstance(SQLiteActivityMulThread.this)
// .updateTable(Integer.toString(age));
// }
// });
// }

}

@Override
protected void onDestroy() {
StuDao.getInstance(SQLiteActivityMulThread.this).destoryDB();
super.onDestroy();
}

// 通过findViewById获得Button对象的方法
private void creatView() {
insertDatabase = (Button) findViewById(R.id.insertDatabase);
updateDatabase1 = (Button) findViewById(R.id.updateDatabase1);
updateDatabase2 = (Button) findViewById(R.id.updateDatabase2);
deleteDatabase = (Button) findViewById(R.id.deleteDatabase);
}

// 为按钮注册监听的方法
private void setListener() {
insertDatabase.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// insertTable
id = 100;
for (; id < 120; id++) {
final int sid = id;
// System.out.println("------sid----"+sid);
// System.out.println("------id----"+id);
MyThreadPoolExecutor.getInstance().execute(new Runnable() {

@Override
public void run() {
StuDao.getInstance(SQLiteActivityMulThread.this)
.insertTable(Integer.toString(sid),
Integer.toString(sid));
StuDao.getInstance(SQLiteActivityMulThread.this)
.queryTable(sid);
}
});
}
}

});
updateDatabase1.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
id = 100;
// updateTable
for (; id < 120; id++) {
final int sid = id;
// System.out.println("------sid----"+sid);
// System.out.println("------id----"+id);
MyThreadPoolExecutor.getInstance().execute(new Runnable() {

@Override
public void run() {
StuDao.getInstance(SQLiteActivityMulThread.this)
.updateTable(Integer.toString(sid),
Integer.toString(sid * 2));
StuDao.getInstance(SQLiteActivityMulThread.this)
.queryTable(sid);
}
});
// MyThreadPoolExecutor.getInstance().execute(new Runnable()
// {
//
// @Override
// public void run() {
// StuDao.getInstance(SQLiteActivityMulThread.this).deleteTable(id);
// }
// });
}
}
});
updateDatabase2.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
id = 100;
for (; id < 120; id++) {
final int sid = id;
// System.out.println("------sid----"+sid);
// System.out.println("------id----"+id);
MyThreadPoolExecutor.getInstance().execute(new Runnable() {

@Override
public void run() {
StuDao.getInstance(SQLiteActivityMulThread.this)
.updateTable(Integer.toString(sid),
Integer.toString(sid * 3));
StuDao.getInstance(SQLiteActivityMulThread.this)
.queryTable(sid);
}
});
// MyThreadPoolExecutor.getInstance().execute(new Runnable()
// {
//
// @Override
// public void run() {
// StuDao.getInstance(SQLiteActivityMulThread.this).deleteTable(id);
// }
// });
}
}

});
deleteDatabase.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
MyThreadPoolExecutor.getInstance().execute(new Runnable() {

@Override
public void run() {
StuDao.getInstance(SQLiteActivityMulThread.this)
.deleteTable();
}
});
}

});

}

// 创建数据库的方法
class CreateListener implements OnClickListener {

@Override
public void onClick(View v) {
// 创建StuDBHelper对象
StuDBHelper dbHelper = new StuDBHelper(
SQLiteActivityMulThread.this, "stu_db", null, 1);
// 得到一个可读的SQLiteDatabase对象
SQLiteDatabase db = dbHelper.getReadableDatabase();
}
}

// 更新数据库的方法
class UpdateListener implements OnClickListener {

@Override
public void onClick(View v) {
// 数据库版本的更新,由原来的1变为2
StuDBHelper dbHelper = new StuDBHelper(
SQLiteActivityMulThread.this, "stu_db", null, 2);
SQLiteDatabase db = dbHelper.getReadableDatabase();
}
}

// 插入数据的方法
class InsertListener implements OnClickListener {

@Override
public void onClick(View v) {

StuDBHelper dbHelper = new StuDBHelper(
SQLiteActivityMulThread.this, "stu_db", null, 1);
// 得到一个可写的数据库
SQLiteDatabase db = dbHelper.getWritableDatabase();

// 生成ContentValues对象 //key:列名,value:想插入的值
ContentValues cv = new ContentValues();
// 往ContentValues对象存放数据,键-值对模式
cv.put("id", 1);
cv.put("sname", "xiaoming");
cv.put("sage", 21);
cv.put("ssex", "male");
// 调用insert方法,将数据插入数据库
db.insert("stu_table", null, cv);
// 关闭数据库
db.close();
}
}

// 查询数据的方法
class QueryListener implements OnClickListener {

@Override
public void onClick(View v) {

StuDBHelper dbHelper = new StuDBHelper(
SQLiteActivityMulThread.this, "stu_db", null, 1);
// 得到一个可写的数据库
SQLiteDatabase db = dbHelper.getReadableDatabase();
// 参数1:表名
// 参数2:要想显示的列
// 参数3:where子句
// 参数4:where子句对应的条件值
// 参数5:分组方式
// 参数6:having条件
// 参数7:排序方式
Cursor cursor = db.query("stu_table", new String[] { "id", "sname",
"sage", "ssex" }, "id=?", new String[] { "1" }, null, null,
null);
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex("sname"));
String age = cursor.getString(cursor.getColumnIndex("sage"));
String sex = cursor.getString(cursor.getColumnIndex("ssex"));
System.out.println("query------->" + "姓名:" + name + " " + "年龄:"
+ age + " " + "性别:" + sex);
}
// 关闭数据库
db.close();
}
}

// 修改数据的方法
class ModifyListener implements OnClickListener {

@Override
public void onClick(View v) {

StuDBHelper dbHelper = new StuDBHelper(
SQLiteActivityMulThread.this, "stu_db", null, 1);
// 得到一个可写的数据库
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put("sage", "100");
// where 子句 "?"是占位符号,对应后面的"1",
String whereClause = "id=?";
String[] whereArgs = { String.valueOf(1) };
// 参数1 是要更新的表名
// 参数2 是一个ContentValeus对象
// 参数3 是where子句
db.update("stu_table", cv, whereClause, whereArgs);
}
}

// 删除数据的方法
class DeleteListener implements OnClickListener {

@Override
public void onClick(View v) {

StuDBHelper dbHelper = new StuDBHelper(
SQLiteActivityMulThread.this, "stu_db", null, 1);
// 得到一个可写的数据库
SQLiteDatabase db = dbHelper.getReadableDatabase();
String whereClauses = "id=?";
String[] whereArgs = { String.valueOf(2) };
// 调用delete方法,删除数据
db.delete("stu_table", whereClauses, whereArgs);
}
}
}


实现自己的线程池:
package com.lingdududu;

import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

//	ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 200, TimeUnit.MILLISECONDS,
//            new LinkedBlockingQueue<Runnable>() );

public class MyThreadPoolExecutor implements Executor {

private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 256;
private static final int KEEP_ALIVE = 1;

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);

@Override
public Thread newThread(Runnable r) {
return new Thread(r, "MyThreadPoolExecutor #"
+ mCount.getAndIncrement());
}
};

private final ThreadPoolExecutor mThreadPoolExecutor;
private static MyThreadPoolExecutor mExecutor;

public static MyThreadPoolExecutor getInstance() {
if (mExecutor == null) {
return new MyThreadPoolExecutor(CORE_POOL_SIZE);
}
return mExecutor;
}

public MyThreadPoolExecutor getInstance(int poolSize) {
if (mExecutor == null) {
return new MyThreadPoolExecutor(poolSize);
}
return mExecutor;
}

private MyThreadPoolExecutor() {
this(CORE_POOL_SIZE);
}

private MyThreadPoolExecutor(int poolSize) {
mThreadPoolExecutor = new ThreadPoolExecutor(poolSize,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(), sThreadFactory);
}

public int getPoolSize() {
return mThreadPoolExecutor.getCorePoolSize();
}

public void setPoolSize(int poolSize) {
if (poolSize > 0) {
mThreadPoolExecutor.setCorePoolSize(poolSize);
}
}

public boolean isBusy() {
return mThreadPoolExecutor.getActiveCount() >= mThreadPoolExecutor
.getCorePoolSize();
}

@Override
public void execute(final Runnable r) {
mThreadPoolExecutor.execute(r);
}
}


不知道大家有没发现在上述的获取数据库的方法中,我都用统一用了getWritableDatabase(),原因是如果在升级了数据库后,如果删除了某一张表,又恰好是利用getReadableDatabase()的方式打开数据库的话,将会报错因为没权限创建修改数据库。详情看我的博客http://blog.csdn.net/u011484134/article/details/49795991

 对于后面这个问题,我暂时也没想到好的办法解决,欢迎大家讨论!

源码

免积分下载 http://download.csdn.net/detail/u011484134/9289557
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息