www.studyyoun.com/study_andro…html
多线程操做数据库,为处理并发问题,你们第一想到的是加锁操做 ,SQLite是文件级别的锁.SQLite3对于并发的处理机制是容许同一个进程的多个线程同时读取一个数据库,可是任什么时候刻只容许一个线程/进程写入数据库。在操行写操做时,数据库文件被琐定,此时任何其余读/写操做都被阻塞,若是阻塞超过5秒钟(默认是5秒,能太重新编译sqlite能够修改超时时间),就报"database is locked"错误java
SQLiteDatabaseLockedException: database is locked和java.lang.IllegalStateException: attempt to re-open an already-closed object.这两个是咱们最多见的数据库并发异常android
SQLiteDatabaseLockedException: database is locked 异常可经过 Android 数据库综述(一) 中的方法,也就是将SqliteHelper对象设置为单例就能够解决sql
// 私有的构造函数,只能本身使用,防止绕过同步方法生成多个实例,
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这两个类来完成bash
CountDownLatch是在java1.5被引入的,跟它一块儿被引入的并发工具类还有CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue,它们都存在于java.util.concurrent包下。CountDownLatch这个类可以使一个线程等待其余线程完成各自的工做后再执行。例如,应用程序的主线程但愿在负责启动框架服务的线程已经启动全部的框架服务以后再执行多线程
CountDownLatch是经过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了本身的任务后,计数器的值就会减1。当计数器值到达0时,它表示全部的线程已经完成了任务,而后在闭锁上等待的线程就能够恢复执行任务。并发
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) {
}复制代码
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;
}复制代码