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

没啥诀窍,只需保证几个线程都是用的一个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();
    }
}



    
    

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对同一个数据库文件的过频繁或同时的打开就会导致错误



你可能感兴趣的:(Android相关)