如何在多线程操作数据库--多线程串行
2015-11-21 17:38
337 查看
前提
很多时候我们都是在主线程下直接打开数据库并对数据库操作的,但如果遇到加密型的数据库,例如SQLCipher加密数据库,或者是对字段进行加密的数据库;你就不得不在线程里面操作数据库了!解决
大家都知道,在多线程操作数据库的时候,如果在各自线程中都利用之前的方法:实例化一个SQLiteOpenHelper类,然后在调用其中的方法。后面的就会遇到android.database.sqlite.SQLiteException:database is locked这样的异常。原因当然就是多线程的时候,导致的关闭异常,例如A线程的一个查询操作,在打开了数据库后,被B线程close了,导致A线程报错。要解决一样的问题,安全一点的方法是利用单例的形式创建SQLiteOpenHelper类或者是实现是具体数据库增删改查的Dao类,并且对方法添加synchronizaed关键字。
关键代码:StuDao 包含了对数据库的操作语句,在方法上都加上了锁
package com.lingdududu.testSQLiteDao; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Build; import android.util.Log; import com.lingdududu.testSQLiteDb.StuDBHelper; public class StuDao { // 创建StuDBHelper对象 StuDBHelper dbHelper = null; // 得到一个可读的SQLiteDatabase对象 SQLiteDatabase db = null; Cursor cursor = null; static StuDao stuDao = null; private String TABLE = "stu_table"; private String TAG = "StuDao"; static Object lock = new Object(); public static StuDao getInstance(Context context) { if (stuDao == null) { synchronized (lock) { if (stuDao == null) { stuDao = new StuDao(context); } } } return stuDao; } public void destoryDB() { // 关闭数据库 close(); stuDao = null; } private StuDao(Context context) { Log.e(TAG, "--->>> StuDao"); dbHelper = new StuDBHelper(context, "stu_db", null, 1); if (Build.VERSION.SDK_INT >= 11) { dbHelper.getWritableDatabase().enableWriteAheadLogging(); } } Object queryLock = new Object(); public synchronized void queryTable() { // 得到一个可写的数据库 db = dbHelper.getWritableDatabase(); // 参数1:表名 // 参数2:要想显示的列 // 参数3:where子句 // 参数4:where子句对应的条件值 // 参数5:分组方式 // 参数6:having条件 // 参数7:排序方式 cursor = db.query(TABLE, new String[] { "id", "sname", "sage", "ssex" }, null, null, 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")); int id = cursor.getInt(cursor.getColumnIndex("id")); System.out.println("query------->" + "id:" + id + " " + "姓名:" + name + " " + "年龄:" + age + " " + "性别:" + sex); } // 关闭数据库 this.close(); } public synchronized void queryTable(int id) { // 得到一个可写的数据库 db = dbHelper.getWritableDatabase(); // 参数1:表名 // 参数2:要想显示的列 // 参数3:where子句 // 参数4:where子句对应的条件值 // 参数5:分组方式 // 参数6:having条件 // 参数7:排序方式 cursor = db.query(TABLE, new String[] { "id", "sname", "sage", "ssex" }, "id=?", new String[] { Integer.toString(id) }, 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")); int sid = cursor.getInt(cursor.getColumnIndex("id")); Log.d(TAG, "id:" + sid + " " + "姓名:" + name + " " + "年龄:" + age + " " + "性别:" + sex); } // 关闭数据库 this.close(); } public synchronized void updateTable(String id, String sage) { // 得到一个可写的数据库 db = dbHelper.getWritableDatabase(); ContentValues cv = new ContentValues(); cv.put("sage", sage); cv.put("id", id); // where 子句 "?"是占位符号,对应后面的"1", String whereClause = "id=?"; String[] whereArgs = { String.valueOf(id) }; // 参数1 是要更新的表名 // 参数2 是一个ContentValeus对象 // 参数3 是where子句 db.update(TABLE, cv, whereClause, whereArgs); // 关闭数据库 this.close(); } public synchronized void insertTable(String id, String sage) { // 得到一个可写的数据库 db = dbHelper.getWritableDatabase(); // 生成ContentValues对象 //key:列名,value:想插入的值 ContentValues cv = new ContentValues(); // 往ContentValues对象存放数据,键-值对模式 cv.put("id", id); cv.put("sname", "xiaoming"); cv.put("sage", sage); cv.put("ssex", "male"); // 调用insert方法,将数据插入数据库 db.insert("stu_table", null, cv); // 关闭数据库 // 关闭数据库 this.close(); } public synchronized void deleteTable(int id) { // 得到一个可写的数据库 db = dbHelper.getWritableDatabase(); String whereClauses = "id=?"; String[] whereArgs = { String.valueOf(id) }; // 调用delete方法,删除数据 db.delete(TABLE, whereClauses, whereArgs); // 关闭数据库 this.close(); } public synchronized void deleteTable() { // db = dbHelper.getWritableDatabase(); // String sql = "drop table if exists "+TABLE; // db.execSQL(sql); // 得到一个可写的数据库 db = dbHelper.getWritableDatabase(); // 调用delete方法,删除数据 db.delete(TABLE, null, null); // 关闭数据库 this.close(); } public void close() { if (db != null) { db.close(); } if (cursor != null) { cursor.close(); } } }
在SQLiteActivityMulThread开启线程操作数据库:
package com.lingdududu.testSQLite; import com.lingdududu.MyThreadPoolExecutor; import com.lingdududu.testSQLiteDao.StuDao; import com.lingdududu.testSQLiteDb.StuDBHelper; import android.app.Activity; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; /* * @author lingdududu */ public class SQLiteActivityMulThread extends Activity { /** Called when the activity is first created. */ // 声明各个按钮 private Button insertDatabase; private Button updateDatabase1; private Button updateDatabase2; private Button deleteDatabase; int age; int id = 100; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_mul); // 调用creatView方法 creatView(); setListener(); // MyThreadPoolExecutor.getInstance().execute(new Runnable() { // // @Override // public void run() { // StuDao.getInstance(SQLiteActivityMulThread.this).queryTable(); // } // }); // for (age = 0; age < 10; age++) { // MyThreadPoolExecutor.getInstance().execute(new Runnable() { // @Override // public void run() { // StuDao.getInstance(SQLiteActivityMulThread.this) // .updateTable(Integer.toString(age)); // } // }); // } } @Override protected void onDestroy() { StuDao.getInstance(SQLiteActivityMulThread.this).destoryDB(); super.onDestroy(); } // 通过findViewById获得Button对象的方法 private void creatView() { insertDatabase = (Button) findViewById(R.id.insertDatabase); updateDatabase1 = (Button) findViewById(R.id.updateDatabase1); updateDatabase2 = (Button) findViewById(R.id.updateDatabase2); deleteDatabase = (Button) findViewById(R.id.deleteDatabase); } // 为按钮注册监听的方法 private void setListener() { insertDatabase.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub // insertTable id = 100; for (; id < 120; id++) { final int sid = id; // System.out.println("------sid----"+sid); // System.out.println("------id----"+id); MyThreadPoolExecutor.getInstance().execute(new Runnable() { @Override public void run() { StuDao.getInstance(SQLiteActivityMulThread.this) .insertTable(Integer.toString(sid), Integer.toString(sid)); StuDao.getInstance(SQLiteActivityMulThread.this) .queryTable(sid); } }); } } }); updateDatabase1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub id = 100; // updateTable for (; id < 120; id++) { final int sid = id; // System.out.println("------sid----"+sid); // System.out.println("------id----"+id); MyThreadPoolExecutor.getInstance().execute(new Runnable() { @Override public void run() { StuDao.getInstance(SQLiteActivityMulThread.this) .updateTable(Integer.toString(sid), Integer.toString(sid * 2)); StuDao.getInstance(SQLiteActivityMulThread.this) .queryTable(sid); } }); // MyThreadPoolExecutor.getInstance().execute(new Runnable() // { // // @Override // public void run() { // StuDao.getInstance(SQLiteActivityMulThread.this).deleteTable(id); // } // }); } } }); updateDatabase2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub id = 100; for (; id < 120; id++) { final int sid = id; // System.out.println("------sid----"+sid); // System.out.println("------id----"+id); MyThreadPoolExecutor.getInstance().execute(new Runnable() { @Override public void run() { StuDao.getInstance(SQLiteActivityMulThread.this) .updateTable(Integer.toString(sid), Integer.toString(sid * 3)); StuDao.getInstance(SQLiteActivityMulThread.this) .queryTable(sid); } }); // MyThreadPoolExecutor.getInstance().execute(new Runnable() // { // // @Override // public void run() { // StuDao.getInstance(SQLiteActivityMulThread.this).deleteTable(id); // } // }); } } }); deleteDatabase.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub MyThreadPoolExecutor.getInstance().execute(new Runnable() { @Override public void run() { StuDao.getInstance(SQLiteActivityMulThread.this) .deleteTable(); } }); } }); } // 创建数据库的方法 class CreateListener implements OnClickListener { @Override public void onClick(View v) { // 创建StuDBHelper对象 StuDBHelper dbHelper = new StuDBHelper( SQLiteActivityMulThread.this, "stu_db", null, 1); // 得到一个可读的SQLiteDatabase对象 SQLiteDatabase db = dbHelper.getReadableDatabase(); } } // 更新数据库的方法 class UpdateListener implements OnClickListener { @Override public void onClick(View v) { // 数据库版本的更新,由原来的1变为2 StuDBHelper dbHelper = new StuDBHelper( SQLiteActivityMulThread.this, "stu_db", null, 2); SQLiteDatabase db = dbHelper.getReadableDatabase(); } } // 插入数据的方法 class InsertListener implements OnClickListener { @Override public void onClick(View v) { StuDBHelper dbHelper = new StuDBHelper( SQLiteActivityMulThread.this, "stu_db", null, 1); // 得到一个可写的数据库 SQLiteDatabase db = dbHelper.getWritableDatabase(); // 生成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.close(); } } // 查询数据的方法 class QueryListener implements OnClickListener { @Override public void onClick(View v) { StuDBHelper dbHelper = new StuDBHelper( SQLiteActivityMulThread.this, "stu_db", null, 1); // 得到一个可写的数据库 SQLiteDatabase db = dbHelper.getReadableDatabase(); // 参数1:表名 // 参数2:要想显示的列 // 参数3:where子句 // 参数4:where子句对应的条件值 // 参数5:分组方式 // 参数6:having条件 // 参数7:排序方式 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")); System.out.println("query------->" + "姓名:" + name + " " + "年龄:" + age + " " + "性别:" + sex); } // 关闭数据库 db.close(); } } // 修改数据的方法 class ModifyListener implements OnClickListener { @Override public void onClick(View v) { StuDBHelper dbHelper = new StuDBHelper( SQLiteActivityMulThread.this, "stu_db", null, 1); // 得到一个可写的数据库 SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues cv = new ContentValues(); cv.put("sage", "100"); // where 子句 "?"是占位符号,对应后面的"1", String whereClause = "id=?"; String[] whereArgs = { String.valueOf(1) }; // 参数1 是要更新的表名 // 参数2 是一个ContentValeus对象 // 参数3 是where子句 db.update("stu_table", cv, whereClause, whereArgs); } } // 删除数据的方法 class DeleteListener implements OnClickListener { @Override public void onClick(View v) { StuDBHelper dbHelper = new StuDBHelper( SQLiteActivityMulThread.this, "stu_db", null, 1); // 得到一个可写的数据库 SQLiteDatabase db = dbHelper.getReadableDatabase(); String whereClauses = "id=?"; String[] whereArgs = { String.valueOf(2) }; // 调用delete方法,删除数据 db.delete("stu_table", whereClauses, whereArgs); } } }
实现自己的线程池:
package com.lingdududu; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; // ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 200, TimeUnit.MILLISECONDS, // new LinkedBlockingQueue<Runnable>() ); public class MyThreadPoolExecutor implements Executor { private static final int CORE_POOL_SIZE = 5; private static final int MAXIMUM_POOL_SIZE = 256; private static final int KEEP_ALIVE = 1; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); @Override public Thread newThread(Runnable r) { return new Thread(r, "MyThreadPoolExecutor #" + mCount.getAndIncrement()); } }; private final ThreadPoolExecutor mThreadPoolExecutor; private static MyThreadPoolExecutor mExecutor; public static MyThreadPoolExecutor getInstance() { if (mExecutor == null) { return new MyThreadPoolExecutor(CORE_POOL_SIZE); } return mExecutor; } public MyThreadPoolExecutor getInstance(int poolSize) { if (mExecutor == null) { return new MyThreadPoolExecutor(poolSize); } return mExecutor; } private MyThreadPoolExecutor() { this(CORE_POOL_SIZE); } private MyThreadPoolExecutor(int poolSize) { mThreadPoolExecutor = new ThreadPoolExecutor(poolSize, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), sThreadFactory); } public int getPoolSize() { return mThreadPoolExecutor.getCorePoolSize(); } public void setPoolSize(int poolSize) { if (poolSize > 0) { mThreadPoolExecutor.setCorePoolSize(poolSize); } } public boolean isBusy() { return mThreadPoolExecutor.getActiveCount() >= mThreadPoolExecutor .getCorePoolSize(); } @Override public void execute(final Runnable r) { mThreadPoolExecutor.execute(r); } }
不知道大家有没发现在上述的获取数据库的方法中,我都用统一用了getWritableDatabase(),原因是如果在升级了数据库后,如果删除了某一张表,又恰好是利用getReadableDatabase()的方式打开数据库的话,将会报错因为没权限创建修改数据库。详情看我的博客http://blog.csdn.net/u011484134/article/details/49795991
对于后面这个问题,我暂时也没想到好的办法解决,欢迎大家讨论!
源码
免积分下载 http://download.csdn.net/detail/u011484134/9289557相关文章推荐
- Android之获取手机上的图片和视频缩略图thumbnails
- Python3写爬虫(四)多线程实现数据爬取
- 谷歌 Project Zero 团队宣布新政策,漏洞披露前将有完整的 90 天缓冲期
- 数据库链接字符串查询网站
- Android实现表情 抓取新浪表情
- 详解Android解析Xml的三种方式——DOM、SAX以及XMLpull
- DB2实例管理
- DB2实例管理
- 保障MySQL数据安全的14个最佳方法
- mysql问答汇集
- 创建一个空的IBM DB2 ECO数据库的方法
- Access 2000 数据库 80 万记录通用快速分页类
- 春节长假安全手册
- 开通一个数据库失败的原因的和解决办法
- 一个简单的asp数据库操作类
- 地震避险自救常识
- CentOS下DB2数据库安装过程详解