关闭

[置顶] 如何在多线程操作数据库--多线程串行

标签: 数据库多线程安全android开发
593人阅读 评论(0) 收藏 举报
分类:

前提

很多时候我们都是在主线程下直接打开数据库并对数据库操作的,但如果遇到加密型的数据库,例如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
 对于后面这个问题,我暂时也没想到好的办法解决,欢迎大家讨论!

源码



0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:30854次
    • 积分:634
    • 等级:
    • 排名:千里之外
    • 原创:32篇
    • 转载:7篇
    • 译文:0篇
    • 评论:4条