您的位置:首页 > 移动开发 > Android开发

Android SQLite多线程读写和线程同步源码分析

2018-03-17 16:56 330 查看
没啥诀窍,只需保证几个线程都是用的一个SQLiteDataBase对象就行了。
如果我们非要在不同线程中用两个或更多的SQLiteDataBase对象呢,当然这些SQLiteDataBase对象所操作的是同一个数据库,也就是同一个db文件,这个就是这篇博客的重点了

(ps:使用到的代码将在博文结尾贴出)

第一种情况:一个SQLiteDataBase对象不同线程

两个子线程执行修改数据库操作,三个子线程执行查询数据库对象, writethread = new SQLWritethread("write",true,sqLiteUtil);
writethread_two = new SQLWritethread("write_two",true,sqLiteUtil);
readThread_one = new SQLReadThread("one",true,sqLiteUtil);
readThread_two = new SQLReadThread("two",true,sqLiteUtil);
readThread_three = new SQLReadThread("three",true,sqLiteUtil);

writethread.start();
writethread_two.start();
readThread_one.start();
readThread_two.start();
readThread_three.start();
03-17 16:33:56.580 28028-28076/com.example.zth.seven V/zzw: write update
03-17 16:33:56.582 28028-28078/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
03-17 16:33:56.582 28028-28078/com.example.zth.seven V/zzw: one query
03-17 16:33:56.595 28028-28077/com.example.zth.seven V/zzw: write_two update
03-17 16:33:56.596 28028-28079/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
03-17 16:33:56.597 28028-28079/com.example.zth.seven V/zzw: two query
03-17 16:33:56.598 28028-28080/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
03-17 16:33:56.598 28028-28080/com.example.zth.seven V/zzw: three query
03-17 16:33:56.601 28028-28076/com.example.zth.seven V/zzw: write update
03-17 16:33:56.602 28028-28078/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:10 性别:male
03-17 16:33:56.602 28028-28078/com.example.zth.seven V/zzw: one query
03-17 16:33:56.605 28028-28077/com.example.zth.seven V/zzw: write_two update
03-17 16:33:56.606 28028-28079/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:10 性别:male
03-17 16:33:56.607 28028-28079/com.example.zth.seven V/zzw: two query
03-17 16:33:56.609 28028-28080/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:10 性别:male
03-17 16:33:56.609 28028-28080/com.example.zth.seven V/zzw: three query

可以看出同一个SQLiteDataBase对象多线程读写操作没问题

第二种情况,两个SQLiteDataBase对象不同线程

一个子线程执行update函数,所持有的SQLiteDataBase对象是sqLiteUtil,而其他三个子线程执行query函数,所持有的SQLiteDataBase对象是sqLiteUtil_two,
writethread = new SQLWritethread("write",true,sqLiteUtil);
//writethread_two = new SQLWritethread("write_two",true,sqLiteUtil);
readThread_one = new SQLReadThread("one",true,sqLiteUtil_two);
readThread_two = new SQLReadThread("two",true,sqLiteUtil_two);
readThread_three = new SQLReadThread("three",true,sqLiteUtil_two);

writethread.start();
writethread_two.start();
readThread_one.start();
readThread_two.start();
readThread_three.start();
因为无限循环的时候没有设置sleep,模仿多次读写操作,所以log很多重复的
03-17 16:39:16.634 29175-29295/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
03-17 16:39:16.634 29175-29295/com.example.zth.seven V/zzw: one query
03-17 16:39:16.636 29175-29296/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
03-17 16:39:16.638 29175-29296/com.example.zth.seven V/zzw: two query
03-17 16:39:16.640 29175-29297/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
03-17 16:39:16.640 29175-29297/com.example.zth.seven V/zzw: three query
03-17 16:39:16.642 29175-29295/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:0 性别:male
。。。。。。
03-17 16:39:16.650 29175-29296/com.example.zth.seven V/zzw: two query
03-17 16:39:16.665 29175-29294/com.example.zth.seven V/zzw: write update
03-17 16:39:16.668 29175-29294/com.example.zth.seven V/zzw: write update
03-17 16:39:16.975 29175-29294/com.example.zth.seven V/zzw: write update
03-17 16:39:16.977 29175-29294/com.example.zth.seven V/zzw: write update
03-17 16:39:16.980 29175-29294/com.example.zth.seven V/zzw: write update
。。。。。。。。。。
03-17 16:39:17.081 29175-29297/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:1060 性别:male
03-17 16:39:17.081 29175-29297/com.example.zth.seven V/zzw: three query
03-17 16:39:17.083 29175-29295/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:1060 性别:male
03-17 16:39:17.083 29175-29295/com.example.zth.seven V/zzw: one query
03-17 16:39:17.083 29175-29296/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:1060 性别:male
03-17 16:39:17.084 29175-29296/com.example.zth.seven V/zzw: two query
03-17 16:39:17.085 29175-29297/com.example.zth.seven V/zzw: query------->姓名:xiaoming 年龄:1060 性别:male
。。。。。。。。。。。

运行正常

两个个子线程执行update函数,所持有的SQLiteDataBase对象是sqLiteUtil,而其他三个子线程执行query函数,所持有的SQLiteDataBase对象是sqLiteUtil_two,
writethread = new SQLWritethread("write",true,sqLiteUtil);
writethread_two = new SQLWritethread("write_two",true,sqLiteUtil);
readThread_one = new SQLReadThread("one",true,sqLiteUtil_two);
readThread_two = new SQLReadThread("two",true,sqLiteUtil_two);
readThread_three = new SQLReadThread("three",true,sqLiteUtil_two);

writethread.start();
writethread_two.start();
readThread_one.start();
readThread_two.start();
readThread_three.start();

一开始几秒还好,然后就说子线程执行的query函数有问题
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (Sqlite code 5), (OS error - 2:No such file or directory)
看来在不同线程下读操作不能在多个SQLiteDataBase对象进行写操作的时候来完成

如果是读操作那边有两个SQLiteDataBase对象呢 writethread = new SQLWritethread("write",true,sqLiteUtil);
//writethread_two = new SQLWritethread("write_two",true,sqLiteUtil);
readThread_one = new SQLReadThread("one",true,sqLiteUtil);
readThread_two = new SQLReadThread("two",true,sqLiteUtil_two);
readThread_three = new SQLReadThread("three",true,sqLiteUtil_two);

writethread.start();
// writethread_two.start();
readThread_one.start();
readThread_two.start();
readThread_three.start();
结果一样
android.database.sqlite.SQLiteCantOpenDatabaseException: unable to open database file (Sqlite code 14), (OS error - 24:Too many open files)

结论:在不同的线程完成读写操作只能使用同一个SQLiteDataBase对象,或者写操作使用一个SQLiteDataBase对象,读操作使用一个SQLiteDataBase对象

代码:
public class SQLiteActivity extends Activity {
/** Called when the activity is first created. */
//声明各个按钮
private Button createBtn;
private Button insertBtn;
private Button updateBtn;
private Button queryBtn;
private Button deleteBtn;
private Button ModifyBtn,btn_thread_start,btn_thread_stop;
private SQLiteUtil sqLiteUtil,sqLiteUtil_two;
private SQLWritethread writethread,writethread_two;
private SQLReadThread readThread_one;
private SQLReadThread readThread_two;
private SQLReadThread readThread_three;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_five);

//调用creatView方法
creatView();
//setListener方法
setListener();
}

//通过findViewById获得Button对象的方法
private void creatView(){
createBtn = (Button)findViewById(R.id.createDatabase);
updateBtn = (Button)findViewById(R.id.updateDatabase);
insertBtn = (Button)findViewById(R.id.insert);
ModifyBtn = (Button)findViewById(R.id.update);
queryBtn = (Button)findViewById(R.id.query);
deleteBtn = (Button)findViewById(R.id.delete);
btn_thread_start = (Button)findViewById(R.id.btn_thread_start);
btn_thread_stop = (Button)findViewById(R.id.btn_thread_stop);
}

//为按钮注册监听的方法
private void setListener(){
createBtn.setOnClickListener(new CreateListener());
updateBtn.setOnClickListener(new UpdateListener());
insertBtn.setOnClickListener(new InsertListener());
ModifyBtn.setOnClickListener(new ModifyListener());
queryBtn.setOnClickListener(new QueryListener());
deleteBtn.setOnClickListener(new DeleteListener());
btn_thread_start.setOnClickListener(new ThreadStartListener());
btn_thread_stop.setOnClickListener(new ThreadStopListener());

sqLiteUtil = new SQLiteUtil(this);
sqLiteUtil_two = new SQLiteUtil(this);
}

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

@Override
public void onClick(View v) {

sqLiteUtil.initSQL();

}
}

//升级数据库的方法
class UpdateListener implements OnClickListener{

@Override
public void onClick(View v) {

sqLiteUtil.upgradeSQL();
}
}

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

@Override
public void onClick(View v) {

sqLiteUtil.insertSQL();

}
}

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

@Override
public void onClick(View v) {
sqLiteUtil.querySQL();

}
}

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

@Override
public void onClick(View v) {

sqLiteUtil.updateSQL();

}
}

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

@Override
public void onClick(View v) {

sqLiteUtil.deleteSQL();

}
}

class ThreadStartListener implements OnClickListener{

@Override
public void onClick(View v) {

writethread = new SQLWritethread("write",true,sqLiteUtil);
//writethread_two = new SQLWritethread("write_two",true,sqLiteUtil);
readThread_one = new SQLReadThread("one",true,sqLiteUtil);
readThread_two = new SQLReadThread("two",true,sqLiteUtil_two);
readThread_three = new SQLReadThread("three",true,sqLiteUtil_two);

writethread.start();
// writethread_two.start();
readThread_one.start();
readThread_two.start();
readThread_three.start();

}
}

class ThreadStopListener implements OnClickListener{

@Override
public void onClick(View v) {

writethread.setState(false);
// writethread_two.setState(false);
readThread_one.setState(false);
readThread_two.setState(false);
readThread_three.setState(false);

}
}

@Override
protected void onDestroy() {

writethread.setState(false);
// writethread_two.setState(false);
readThread_one.setState(false);
readThread_two.setState(false);
readThread_three.setState(false);
sqLiteUtil_two.closeSQL();
sqLiteUtil.closeSQL();

super.onDestroy();
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="hello"
/>
<Button
android:id="@+id/createDatabase"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="创建数据库"
/>
<Button
android:id="@+id/updateDatabase"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="更新数据库"
/>
<Button
android:id="@+id/insert"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="插入数据"
/>
<Button
android:id="@+id/update"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="更新数据"
/>
<Button
android:id="@+id/query"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="查询数据"
/>
<Button
android:id="@+id/delete"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="删除数据"
/>

<Button
android:id="@+id/btn_thread_start"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="开启多线程"
/>

<Button
android:id="@+id/btn_thread_stop"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="停止多线程"
/>
</LinearLayout>

public class SQLiteUtil {

private Context context;
private SQLiteDatabase db;

public SQLiteUtil(Context context){
this.context = context;
StuDBHelper dbHelper = new StuDBHelper(context,"stu_db",null,1);
//得到一个可写的数据库
db =dbHelper.getReadableDatabase();
}

public void closeSQL(){
if(db != null&& db.isOpen()){
db.close();
}

}

public void initSQL(){
//创建StuDBHelper对象
StuDBHelper dbHelper = new StuDBHelper(context,"stu_db",null,1);
//得到一个可读的SQLiteDatabase对象
SQLiteDatabase db =dbHelper.getReadableDatabase();
}

public void upgradeSQL(){
// 数据库版本的更新,由原来的1变为2
StuDBHelper dbHelper = new StuDBHelper(context,"stu_db",null,2);
SQLiteDatabase db =dbHelper.getReadableDatabase();
}
public void deleteSQL(){

db.beginTransaction();  //手动设置开始事务
try{
//批量处理操作
String whereClauses = "id=?";
String [] whereArgs = {String.valueOf(1)};
//调用delete方法,删除数据
db.delete("stu_table", whereClauses, whereArgs);
db.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交
}catch(Exception e){
}finally{
db.endTransaction(); //处理完成
}
}

public void insertSQL(){

db.beginTransaction();  //手动设置开始事务
try{
//批量处理操作
//生成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.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交
}catch(Exception e){
}finally{
db.endTransaction(); //处理完成
}
}

public void updateSQL(){
db.beginTransaction();  //手动设置开始事务
try{
//批量处理操作
ContentValues cv = new ContentValues();
cv.put("sage", "23");
//where 子句 "?"是占位符号,对应后面的"1",
String whereClause="id=?";
String [] whereArgs = {String.valueOf(1)};
//参数1 是要更新的表名
//参数2 是一个ContentValeus对象
//参数3 是where子句
db.update("stu_table", cv, whereClause, whereArgs);
db.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交
}catch(Exception e){
}finally{
db.endTransaction(); //处理完成
}
}

public void updateSQL(int sum){
db.beginTransaction();  //手动设置开始事务
try{
//批量处理操作
ContentValues cv = new ContentValues();
cv.put("sage", ""+sum);
//where 子句 "?"是占位符号,对应后面的"1",
String whereClause="id=?";
String [] whereArgs = {String.valueOf(1)};
//参数1 是要更新的表名
//参数2 是一个ContentValeus对象
//参数3 是where子句
db.update("stu_table", cv, whereClause, whereArgs);
db.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交
}catch(Exception e){
}finally{
db.endTransaction(); //处理完成
}
}

public void querySQL(){
//参数1:表名
//参数2:要想显示的列
//参数3:where子句
//参数4:where子句对应的条件值
//参数5:分组方式
//参数6:having条件
//参数7:排序方式
db.beginTransaction();  //手动设置开始事务
try{
//批量处理操作
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"));
Log.v("zzw","query------->" + "姓名:"+name+" "+"年龄:"+age+" "+"性别:"+sex);
}
db.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交
}catch(Exception e){
}finally{
db.endTransaction(); //处理完成
}
}
}

public class SQLReadThread extends Thread {

private boolean state = true;
private SQLiteUtil sqLiteUtil;
public void setState(boolean state){
this.state = state;
}

public SQLReadThread(String name,boolean state,SQLiteUtil sqLiteUtil) {
super(name);
this.state = state;
this.sqLiteUtil = sqLiteUtil;
}

@Override
public void run() {
for (int i = 0;state; i++) {

sqLiteUtil.querySQL();
Log.v("zzw",getName()+" query");
}

}
}

public class SQLWritethread extends Thread {

private boolean state = true;
private SQLiteUtil sqLiteUtil;
public void setState(boolean state){
this.state = state;
}

public SQLWritethread(String name,boolean state,SQLiteUtil sqLiteUtil) {
super(name);
this.state = state;
this.sqLiteUtil = sqLiteUtil;
}

@Override
public void run() {

for (int i = 0;state; i++) {

if(getName().equals("write_two"))
sqLiteUtil.updateSQL(i*10);
else
sqLiteUtil.updateSQL(i*10);
Log.v("zzw",getName()+" update");
}
}
}

public class StuDBHelper extends SQLiteOpenHelper {

private static final String TAG = "TestSQLite";
public static final int VERSION = 1;

//必须要有构造函数
public StuDBHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
}

// 当第一次创建数据库的时候,调用该方法
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "create table stu_table(id int,sname varchar(20),sage int,ssex varchar(10))";
//输出创建数据库的日志信息
Log.i(TAG, "create Database------------->");
//execSQL函数用于执行SQL语句
db.execSQL(sql);
}

//当更新数据库的时候执行该方法
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//输出更新数据库的日志信息
Log.i(TAG, "update Database------------->");
}
}

题外话:你是不是觉得我应该使用getWriteableDataBase,但我可以负责的告诉你基本两种函数得到的SQLiteDatabase对象都能进行读写操作,只会在存储空间不够的情况下才有点区别。(ps:我的64G大存储可能吗)

SQLite线程同步源码分析

加点东西,看了点源码分析了一下SQLite如何完成线程同步,我的切入点就是SQLite如何自己实现的线程同步
我们首先分析读操作也就是query命令如何实现线程同步,跟着我command加鼠标左键看源码

首先query方法里又放了一个query,很正常,加默认参数,(ps:源码在SQLiteDatabase类)
public Cursor query(String table, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having,
String orderBy) {

return query(false, table, columns, selection, selectionArgs, groupBy,
having, orderBy, null /* limit */);
}
还是加默认参数,(ps:源码在SQLiteDatabase类) public Cursor query(boolean distinct, String table, String[] columns,
String selection, String[] selectionArgs, String groupBy,
String having, String orderBy, String limit) {
return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
groupBy, having, orderBy, limit, null);
}
到了真正实现query方法的源码了(ps:源码在SQLiteDatabase类)
public Cursor queryWithFactory(CursorFactory cursorFactory,
boolean distinct, String table, String[] columns,
String selection, String[] selectionArgs, String groupBy,
String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
acquireReference();
try {
String sql = SQLiteQueryBuilder.buildQueryString(
distinct, table, columns, selection, groupBy, having, orderBy, limit);

return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
findEditTable(table), cancellationSignal);
} finally {
releaseReference();
}
}
看到了acquireReference()和releaseReference(),不是感觉很可疑,不要急,我们再看看rawQueryWithFactory方法的源码(ps:源码在SQLiteDatabase类)
public Cursor rawQueryWithFactory(
CursorFactory cursorFactory, String sql, String[] selectionArgs,
String editTable, CancellationSignal cancellationSignal) {
acquireReference();
try {
SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
cancellationSignal);
return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
selectionArgs);
} finally {
releaseReference();
}
}
又看到了这个两个函数,但是我们还要看看driver.query源码(ps:源码在SQLiteCursorDriver类)
Cursor query(CursorFactory factory, String[] bindArgs);
没了,只是个接口函数,好了我们再来看看acquireReference()和releaseReference()的源码,(ps:源码在SQLiteClosable类)
public void acquireReference() {
synchronized(this) {
if (mReferenceCount <= 0) {
throw new IllegalStateException(
"attempt to re-open an already-closed object: " + this);
}
mReferenceCount++;
}
}

public void releaseReference() {
boolean refCountIsZero = false;
synchronized(this) {
refCountIsZero = --mReferenceCount == 0;
}
if (refCountIsZero) {
onAllReferencesReleased();
}
}
感觉是通过Synchronized同步锁锁住自己,来完成线程同步,因为SQLiteDatabase是这个抽象类SQLiteClosable的子类,还不能做判断,再看看onAllReferencesReleased()源码
protected abstract void onAllReferencesReleased();
抽象函数,。。。。。。没关系,看看SQLiteDatabase如何实现这个抽象函数
@Override
protected void onAllReferencesReleased() {
dispose(false);
}

private void dispose(boolean finalized) {
final SQLiteConnectionPool pool;
synchronized (mLock) {
if (mCloseGuardLocked != null) {
if (finalized) {
mCloseGuardLocked.warnIfOpen();
}
mCloseGuardLocked.close();
}

pool = mConnectionPoolLocked;
mConnectionPoolLocked = null;
}

if (!finalized) {
synchronized (sActiveDatabases) {
sActiveDatabases.remove(this);
}

if (pool != null) {
pool.close();
}
}
}

又是同步锁,来防止同时执行多次close(),源码就看到这里,我们再看看官方说明,(PS:百度翻译的)

     acquireReference()
获取对对象的引用。

     close()
释放一个对对象的引用,如果最后一个引用被释放,则关闭对象。

     releaseReference()
释放一个对对象的引用,如果最后一个引用被释放,则关闭对象。

但是这里我们注意一下mReferenceCount的默认值为1

    private int mReferenceCount = 1;

acquireReference()和releaseReference()时是成双成对的出现,一个加,一个减,除非我们调用一次close,否则他不会关闭对象的

结论:SQLiteDatabase通过同步锁锁自己的对象来完成在多线程下读写线程同步,并且通过控制mReferenceCount来中断读写操作,因为锁住的是对象所以对于不同对象的SQLiteDatabase对同一个数据库文件的过频繁或同时的打开就会导致错误

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: