Android 数据库综述(二) 程序计算器与信号量来处理多线程并发问题

原创 2017年10月17日 15:20:40

Android 数据库综述(二) 程序计算器与信号量来处理多线程并发问题


多线程操作数据库,为处理并发问题,大家第一想到的是加锁操作 ,SQLite是文件级别的锁.SQLite3对于并发的处理机制是允许同一个进程的多个线程同时读取一个数据库,但是任何时刻只允许一个线程/进程写入数据库。在操行写操作时,数据库文件被琐定,此时任何其他读/写操作都被阻塞,如果阻塞超过5秒钟(默认是5秒,能过重新编译sqlite可以修改超时时间),就报”database is locked”错误

SQLiteDatabaseLockedException: database is locked和java.lang.IllegalStateException: attempt to re-open an already-closed object.这两个是我们最常见的数据库并发异常

SQLiteDatabaseLockedException: database is locked 异常可通过 Android 数据库综述(一) 中的方法,也就是将SqliteHelper对象设置为单例就可以解决

    // 私有的构造函数,只能自己使用,防止绕过同步方法生成多个实例,
    private SqlDBHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

     /私有的静态对象,为整个应用程序提供一个sqlite操作的静态实例,
    //并保证只能通过下面的静态方法getHelper(Context context)获得,
    //防止使用时绕过同步方法改变它
    //这里主要解决死锁问题,是static就能解决死锁问题
    private static SqlDBHelper instance;

    /**
     * 为应用程序提供一个单一的入口,保证应用程序使用同一个对象操作数据库,不会因为对象不同而使同步方法失效
     * 其实就是获取数据库操作的实例
     * @param context 上下文
     * @return instance
     */
    public static SqlDBHelper getHelper(Context context) {
        if (instance == null)
            instance = new SqlDBHelper(context);
        return instance;
    }

线程A打开数据,正在使用数据库,这时cpu片段分到线程B,线程A挂起。线程B进入执行获取打开db时没有问题,线程B进行操作,在片段时间内数据操作完成,最后关闭数据库database.close()。线程B执行结束,线程A执行,插入数据或者其他操作。。。我靠,怎么数据库关闭了呢,然后抛出java.lang.IllegalStateException: attempt to re-open an already-closed object异常

加同步锁是其中的一种解决方案,我这里提供一种更优雅的方式来处理 并发问题,就是使用计数器原理 ,结合 CountDownLatch 与 Semaphore这两个类来完成

CountDownLatch

CountDownLatch是在java1.5被引入的,跟它一起被引入的并发工具类还有CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue,它们都存在于java.util.concurrent包下。CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行

CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

Semaphore

Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态

// 创建一个计数阈值为5的信号量对象
// 只能5个线程同时访问
Semaphore semp = new Semaphore(5);
try {
    // 申请许可
    semp.acquire();
    try {
        // 业务逻辑
    } catch (Exception e) {

    } finally {
        // 释放许可
        semp.release();
    }
} catch (InterruptedException e) {

}
CountDownLatch 与 Semaphore 结合对数据的增册改查
public class ArtSqlDBHelper  {
    //数据库实例对象 
    private SQLiteDatabase mLiteDatabase;
    //数据库辅助类操作对象
    protected static final SqlDBHelper mSqlDBHelper;
    /**
     * CountDownLatch是JAVA提供在java.util.concurrent包下的一个辅助类,
     * 可以把它看成是一个计数器,其内部维护着一个count计数,只不过对这个计数器的操作都是原子操作,同时只能有一个线程去操作这个计数器,
     * CountDownLatch通过构造函数传入一个初始计数值,调用者可以通过调用CounDownLatch对象的cutDown()方法,来使计数减1;
     * 如果调用对象上的await()方法,那么调用者就会一直阻塞在这里,直到别人通过cutDown方法,将计数减到0,才可以继续执行
     */
    /**
     * 在这里创建了一个计数器,初始值为0 ,也就是说当前有0个线程在操作
     */
    private static CountDownLatch latch =
            new CountDownLatch(0);
    /**
     * 创建一个信号量,初始值为1 只允许一个线程来操作
     * 通过初始值为1的Semaphore,很好的实现了资源的互斥访问
     */
    private static Semaphore lock =
            new Semaphore(1, true);


    public ArtSqlDBHelper(Context context) {
        super(context);
        //通过内部方法获取静态对象
        mSqlDBHelper =SqlDBHelper.getHelper(context);
    }
}

这里是执行的是批量操作,与单条数据的操作思想一至

public void insertArtList(List<ArtModel> artModelList) {

   try {
       if (latch.getCount() == 0 || mLiteDatabase == null) {
           mLiteDatabase = mSqlDBHelper.getWritableDatabase();
       }
       //await()方法,那么调用者就会一直阻塞在这里,直到别人通过cutDown方法,将计数减到0
       latch.await();
       //请求许可 
       lock.acquire();
       // 开启事务
       mLiteDatabase.beginTransaction();

       // 循环插入数据
       for (ArtModel artModel : artModelList) {
           //构建 ContentValues
            ContentValues classValues = new ContentValues();
            classValues.put("art_id", artModel.getId());
            classValues.put("user_id", articlPariseModel);
            //执行操作
            mLiteDatabase.insert("t_art_list", null, classValues);     
       }
       // 操作成功
       mLiteDatabase.setTransactionSuccessful();

   } catch (InterruptedException e) {
       e.printStackTrace();
   } finally {
       //结束事务
       mLiteDatabase.endTransaction();
       //释放许可
       lock.release();
       if (latch.getCount() == 1) {
           //关闭数据库
           mLiteDatabase.close();
           mLiteDatabase = null;
       }
       //计数器减1
       latch.countDown();
   }
}
/**
* 删除
* 清除数据库中的数据
*/
public void clearArtDb() {
   try {
       if (latch.getCount() == 0 || mLiteDatabase == null) {
           mLiteDatabase = mSqlDBHelper.getWritableDatabase();
       }
       latch.await();
       lock.acquire();
   } catch (InterruptedException e) {
       e.printStackTrace();
   }
   //这里没指定 where 限定条件,删除表中的的所有的数据
   String clearsQL = "delete from  t_art_list ";
   //执行
   mLiteDatabase.execSQL(clearsQL);
   lock.release();
   if (latch.getCount() == 1) {
       mLiteDatabase.close();
       mLiteDatabase = null;
   }
   latch.countDown();
}
public void updateArtList(List<ArtModel> artModelList) {

   try {
       if (latch.getCount() == 0 || mLiteDatabase == null) {
           mLiteDatabase = mSqlDBHelper.getWritableDatabase();
       }
       latch.await();
       lock.acquire();


       // 开启事务
       mLiteDatabase.beginTransaction();

       // 循环更新数据
       for (ArtModel artModel : artModelList) {
           //构建 ContentValues
            ContentValues classValues = new ContentValues();
            classValues.put("art_id", artModel.getId());
            classValues.put("user_id", articlPariseModel);

            //执行操作
            mLiteDatabase.update("t_art_list", classValues, "art_id=?", new String[]{String.valueOf(artModel.getId())}); 
       }
       // 操作成功
       mLiteDatabase.setTransactionSuccessful();

   } catch (InterruptedException e) {
       e.printStackTrace();
   } finally {
       mLiteDatabase.endTransaction();
       lock.release();

       if (latch.getCount() == 1) {
           mLiteDatabase.close();
           mLiteDatabase = null;
       }

       latch.countDown();
   }
}
public List<ArtModel> queryArtModelList(int type) {
   try {
       if (latch.getCount() == 0 || mLiteDatabase == null) {
           mLiteDatabase = mSqlDBHelper.getReadableDatabase();
       }
       latch.await();
       lock.acquire();
   } catch (InterruptedException e) {
       e.printStackTrace();
   }
   List<ArtModel> list = new ArrayList<>();


   String sql = "select * from " + SqlDBHelper.TABLE_NAME_ART;
   if (type == 0) {
       sql = sql + " where art_is_top=1 or art_is_recommend=1";
   }

   Cursor cursor = mLiteDatabase.rawQuery(sql, null);
   if (cursor != null) {
       while (cursor.moveToNext()) {
           ArtModel artModel = new ArtModel();


           artModel.id = cursor.getInt(cursor.getColumnIndex("art_id"));
           artModel.artName = cursor.getString(cursor.getColumnIndex("art_name"));

           ... ...

           list.add(artModel);
       }
   }

   lock.release();

   if (latch.getCount() == 1) {
       mLiteDatabase.close();
       mLiteDatabase = null;
   }

   latch.countDown();

   return list;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

android多线程并发协调semaphore机制

在项目开发过程中我们难免用到多线程机制,但不可否认的是如果对公共资源共同访问时候没有处理好线程同步的话很容易出现非线程安全的问题,会带来不可预知的错误,在java中进行线程同步的话一般都用wait和n...
  • limaofang
  • limaofang
  • 2016年04月20日 17:45
  • 562

利用信号量机制解决进程同步和互斥问题

利用信号量机制解决进程同步和互斥问题   在讨论如何用信号量机制解决这个问题之前,我们应该先了解进程同步和互斥间的一些概念。 首先是进程间的两种关系:同步和互斥。所谓同步就是把异步环境下的一组并...
  • sinat_14840443
  • sinat_14840443
  • 2014年10月27日 15:35
  • 1459

多线程、并发及线程的基础问题

1. Java 中能创建 volatile 数组吗? 能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组。 我的意思是,如果改变引用指向的数组,将会受到 ...
  • RING0LYY
  • RING0LYY
  • 2015年12月16日 15:40
  • 2660

Android多线程并发访问网络

最近公司接入了心率设备,把心率传入到服务器,很轻松地完成了这个工作,但是由于没有压力测试的专业人员,测试的任务就落到了我的身上,没办法谁让Android开发人员是个全才呢!! 先说说新的测试要求吧,...
  • yanglei0917
  • yanglei0917
  • 2016年10月19日 13:57
  • 2718

操作系统原理与实践7-信号量的实现和应用

操作系统原理与实践”实验报告 信号量的实现和应用 信号量的实现和应用 实验目的 .加深对进程同步与互斥概念的认识; .掌握信号量的使用,并应用它解决生产者——消费者问题; .掌...
  • Watson2016
  • Watson2016
  • 2017年05月13日 16:35
  • 309

android sqlite数据库并发问题的详细描述和解决方案

线程A打开数据,正在使用数据库,这时cpu片段分到线程B,线程A挂起。线程B进入执行获取打开db时没有问题,线程B进行操作,在片段时间内数据操作完成,最后关闭数据库database.close()。线...
  • nightcurtis
  • nightcurtis
  • 2015年01月22日 09:21
  • 4959

linux 进程间通信二 信号量以及实例

信号量 代码来自:嵌入式linux应用开发标准教程 信号量 http://www.cnblogs.com/hjslovewcl/archive/2011/03/03/2314341....
  • liang890319
  • liang890319
  • 2012年12月11日 09:35
  • 4471

FreeRTOS 的互斥信号量与二进制信号量

URL: http://bbs.ednchina.com/BLOG_ARTICLE_145889.HTM
  • wanruoqingkong
  • wanruoqingkong
  • 2014年06月30日 16:02
  • 806

Android进阶系列11-Android多线程并发

多线程并发的性能问题,介绍了 AsyncTask,HandlerThread,IntentService 与 ThreadPool 分别适合的使用场景以及各自的使用注意事项...
  • u011026779
  • u011026779
  • 2016年09月04日 19:22
  • 297

C/C++经典程序训练3---模拟计算器

Problem Description 简单计算器模拟:输入两个整数和一个运算符,输出运算结果。 Input 第一行输入两个整数,用空格分开; 第二行输入一个运算符(+、-、*、/)。 所...
  • be_your_back
  • be_your_back
  • 2016年10月23日 16:02
  • 3030
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android 数据库综述(二) 程序计算器与信号量来处理多线程并发问题
举报原因:
原因补充:

(最多只允许输入30个字)