// Thread 2 Context context = getApplicationContext(); DatabaseHelper helper = new DatabaseHelper(context); SQLiteDatabase database = helper.getWritableDatabase(); database.insert(…); database.close();
而后在你的Logcat中将输出相似下面的日志信息,而你的写数据操做将会无效。java
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
上面问题的出现,源于你每建立一个 SQLiteOpenHelper 对象时,实际上也是在新建一个数据库链接。若是你尝试经过多个链接同时对数据库进行写数据操做,其必定会失败。android
为确保咱们能在多线程中安全地操做数据库,咱们须要保证只有一个数据库链接被占用。git
咱们先编写一个负责管理单个 SQLiteOpenHelper 对象的单例 DatabaseManager 。 github
public class DatabaseManager { private static DatabaseManager instance; private static SQLiteOpenHelper mDatabaseHelper; public static synchronized void initialize(Context context, SQLiteOpenHelper helper) { if (instance == null) { instance = new DatabaseManager(); mDatabaseHelper = helper; } } public static synchronized DatabaseManager getInstance() { if (instance == null) { throw new IllegalStateException(DatabaseManager.class.getSimpleName() + " is not initialized, call initialize(..) method first."); } return instance; } public synchronized SQLiteDatabase getDatabase() { return new mDatabaseHelper.getWritableDatabase(); }}
为了能在多线程中进行写数据操做,咱们得修改一下代码,具体以下: sql
// In your application class DatabaseManager.initializeInstance(getApplicationContext()); // Thread 1 DatabaseManager manager = DatabaseManager.getInstance(); SQLiteDatabase database = manager.getDatabase() database.insert(…); database.close(); // Thread 2 DatabaseManager manager = DatabaseManager.getInstance(); SQLiteDatabase database = manager.getDatabase() database.insert(…); database.close();
而后又致使另个崩毁数据库
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase
既然咱们只有一个数据库链接,Thread1 和 Thread2 对方法 getDatabase() 的调用就会取得同样的 SQLiteDatabase 对象实例。以后的事情就是,当 Thread1 尝试管理数据库链接时,Thread2 却仍然在使用该数据库链接。这也就是致使 IllegalStateException 崩毁的缘由。安全
所以咱们只能在确保数据库没有再被占用的状况下,才去关闭它。在 stackoveflow 上有一些讨论推荐“永不关闭”你的 SQLiteDatabase 。 若是你这样作,你的logcat将会出现如下的信息,所以我不认为这是一个好主意。多线程
Leak foundCaused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed
示例:并发
public class DatabaseManager { private AtomicInteger mOpenCounter = new AtomicInteger(); private static DatabaseManager instance; private static SQLiteOpenHelper mDatabaseHelper; private SQLiteDatabase mDatabase; public static synchronized void initializeInstance(SQLiteOpenHelper helper) { if (instance == null) { instance = new DatabaseManager(); mDatabaseHelper = helper; } } public static synchronized DatabaseManager getInstance() { if (instance == null) { throw new IllegalStateException(DatabaseManager.class.getSimpleName() + " is not initialized, call initializeInstance(..) method first."); } return instance; } public synchronized SQLiteDatabase openDatabase() { if(mOpenCounter.incrementAndGet() == 1) { // Opening new database mDatabase = mDatabaseHelper.getWritableDatabase(); } return mDatabase; } public synchronized void closeDatabase() { if(mOpenCounter.decrementAndGet() == 0) { // Closing database mDatabase.close(); } }}
而后你能够怎样子去调用它:app
SQLiteDatabase database = DatabaseManager.getInstance().openDatabase();database.insert(...);// database.close(); Don't close it directly!DatabaseManager.getInstance().closeDatabase(); // correct way
之后每当你须要使用数据库链接,你能够经过调用类 DatabaseManager 的方法openDatabase()。在方法里面,内置一个标志数据库被打开多少次的计数器。若是计数为1,表明咱们须要打开一个新的数据库链接,不然,数据库链接已经存在。
在方法 closeDatabase() 中,状况也同样。每次咱们调用 closeDatabase() 方法,计数器都会递减,直到计数为0,咱们就须要关闭数据库链接了。
提示: 你应该使用 AtomicInteger 来处理并发的状况
如今你能够线程安全地使用你的数据库链接了。