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方法,将计数减到0latch.await();//请求许可 lock.acquire();// 开启事务mLiteDatabase.beginTransaction();// 循环插入数据for (ArtModel artModel : artModelList) {//构建 ContentValuesContentValues 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;}//计数器减1latch.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) {//构建 ContentValuesContentValues 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 数据库综述(二) 程序计算器与信号量来处理多线程并发问题相关推荐

  1. Android 数据库综述(一) 数据库片的升级与数据的迁移操作

    Android 数据库综述(一) 数据库片的升级与数据的迁移操作 SQLiteOpenHelper 是 Android平台提供给我们一个数据库辅助类来创建或打开数据库 onCreate(SQLiteD ...

  2. Android 第十二课 使用LitePal操作数据库(记得阅读最后面的注意事项哦)

    一.LitePal简介 1.(新建项目LitePalTest) 正式接触第一个开源库---LitePal LitePal是一款开源的Android 数据库框架,它采用了对象关系映射(ORM)的模式. ...

  3. android安全问题(二) 程序锁

    导读:本文介绍如何实现对应用加锁的功能,无须root权限 某些人有时候会有这样一种需求,小A下载了个软件,只是软件中的美女过于诱惑与暴露,所以他不想让别人知道这是个什么软件,起码不想让别人打开浏览.而 ...

  4. Android数据库高手秘籍(二):创建表和LitePal的基本用法

    原文:http://blog.jobbole.com/77157/ 上一篇文章中我们学习了一些Android数据库相关的基础知识,和几个颇为有用的SQLite命令,都是直接在命令行操作的.但是我们都知 ...

  5. Android 使用SQLiteDatabase操作SQLite数据库(二)

    转自http://www.eoeandroid.com/thread-83428-1-1.html 除了前面给大家介绍的execSQL()和rawQuery()方法, SQLiteDatabase还专 ...

  6. 《Android应用开发攻略》——1.15 程序:Android OS下的小费计算器Tipster

    1.15 程序:Android OS下的小费计算器Tipster Sunit Katkar 1.15.1 问题 当你和朋友前往饭店就餐并且希望计算各自的账单和小费时,可能陷入许多手动计算和分歧之中.你 ...

  7. Android之获取应用程序(包)的大小-----PackageManager的使用(二)

    http://blog.csdn.net/qinjuning/article/details/6892054 通过第一部分 << Android中获取应用程序(包)的信息-----Pack ...

  8. android数据库三个方法有哪些,如何将Android数据库操作通用化(二)

    概述 接着上回的说,虽然我们已经找出了阻挡我们通用化Android数据库操作的五个问题,但是现在我们还不能立即开始去解决这些问题. 试想一下,我们有一个News,那么,相应的就会有NewsDao和Ne ...

  9. 转载-Android数据库高手秘籍(一)——SQLite命令

     原文地址: http://blog.csdn.net/guolin_blog/article/details/38461239 Android数据库高手秘籍(一)--SQLite命令 分类: And ...

最新文章

  1. OSChina 周日乱弹 —— 来和养生的技术负责人决斗!
  2. 搜索引擎名字引发的思考
  3. 软件回归测试及其实践
  4. OpenGL Assimp模型加载库
  5. .net core 5 IIS Api网站部署需要注意
  6. js 调用webservice接口
  7. Linux shell初识及权限理解
  8. Linux学习8-CentOS部署自己本地的django项目
  9. springboot 使用webflux响应式开发教程(二)
  10. 【转载】]基于RedHatEnterpriseLinux V7(RHEL7)下SPEC CPU 2006环境搭建以及测试流程 介绍、安装准备、安装、config文件以及运行脚本介绍...
  11. 基带信号传输之码间串扰
  12. 教妹学Java(十):Unicode字符集简介
  13. 详解威佐夫博弈(POJ1067)
  14. 【2022刷题】受伤的皇后
  15. QQ动态表情包如何制作, 制作软件哪个好
  16. 【USACO题库】3.2.4 Feed Ratios饲料调配
  17. NETDMIS5.0位置度评价案例1
  18. 记一次从某多多上买的斐讯N1黑盒的电视盒子刷机经历
  19. python画饼图程序_Scribus中的Python脚本:制作饼图
  20. Winform UI界面设计例程——TreeView控件

热门文章

  1. 基于深度学习的图像修补/完整方法分析
  2. 快了!CVPR 2019 所有录用论文题目列表刊出,即将开放下载!
  3. nonlocal python3_Python 中的 global、nonlocal 辨析
  4. ARM全新Armv9架构:10年最大更新、增强AI和security能力
  5. 120余家自动驾驶公司的行业汇总
  6. 写给小白的机器学习之数据表示与特征工程详解(附实战代码)
  7. MLSQL解决了什么问题
  8. 深度学习(七十一)3D CNN时空特征学习
  9. 数据结构(二)冒泡排序
  10. 从零开始编写深度学习库(五)Eigen Tensor学习笔记2.0