我正在尝试获得一个对我的sqlite数据库的多线程访问没有失败的模式.另外,让我疯狂的是我无法重现这个问题.
我有一个使用数据库的应用程序,还有Android帐户和Android同步来同步我的应用程序的数据.我的猜测是,当两者同时发生时,它会崩溃.我遇到了很多错误:
* android.database.sqlite.sqliteDatabaseLockedException: database is locked
* android.database.sqlite.sqliteDatabaseLockedException: database is locked (code 5)
* android.database.sqlite.sqliteDatabaseLockedException: error code 5: database is locked
* android.database.sqlite.sqliteDatabaseLockedException: database is locked (code 5):,while compiling: PRAGMA journal_mode
* android.database.sqlite.sqliteDiskIOException: disk I/O error (code 778)
* android.database.sqlite.sqliteException: Failed to change locale for db '/data/data/net.bicou.redmine/databases/redmine.db' to 'en_US'. \n Caused by: android.database.sqlite.sqliteDatabaseLockedException: database is locked (code 5)
也许并非所有这些都与相同的根本原因有关,但我有点迷失.
我有的是:
>一个抽象基类,DbAdapter,由想要管理单个表的子类扩展
>一个管理sqlite数据库的类,名为DbManager,包含一个Lock
现在,用户有一个不是单身的DbManager版本.我打算让DbManager成为单例,以便所有线程共享同一个对象.这不应该是一个问题,因为据我所知,后台同步和应用程序共享相同的过程.
这是课程(只有相关部分):
public abstract class DbAdapter {
Context mContext;
protected DbManager mDbManager;
sqliteDatabase mDb;
public static final String KEY_ROWID = "_id";
public DbAdapter(final Context ctx) {
mContext = ctx;
}
public DbAdapter(final DbAdapter other) {
mContext = other.mContext;
mDb = other.mDb;
mDbManager = other.mDbManager; // removed with singleton version
}
public synchronized DbAdapter open() throws sqlException {
if (mDb != null) {
return this;
}
mDbManager = new DbManager(mContext); // currently in production
mDbManager = DbManager.instance(mContext); // currently investigating this singleton solution
try {
mDb = mDbManager.getWritableDatabase();
} catch (final sqlException e) {
L.e("Unable to open DB,trying again in 1 second",e);
try {
Thread.sleep(1000);
} catch (final InterruptedException e1) {
L.e("Could not wait 1 second " + e1);
}
mDb = mDbManager.getWritableDatabase();// This may crash
}
return this;
}
public synchronized void close() {
mDbManager.close();
mDbManager = null;
mDb = null;
}
}
需要处理数据库表的类将扩展DbAdapter,并实现select,insert,delete等方法.
public class DbManager extends sqliteOpenHelper {
private static final String DB_FILE = "db";
private static final int DB_VERSION = 15;
Context mContext;
Lock mLock = new ReentrantLock();
// Currently in prod
public DbManager(final Context context) {
super(context,DB_FILE,null,DB_VERSION);
mContext = context;
}
// singleton version will make this constructor private and add:
private static DbManager mInstance;
public static synchronized DbManager instance(Context context) {
if (instance == null) {
instance = new DbManager(context);
}
return instance;
}
@Override
public sqliteDatabase getWritableDatabase() {
mLock.lock();
return super.getWritableDatabase();
}
@Override
public void close() {
super.close();
mLock.unlock();
}
@Override
public void onCreate(final sqliteDatabase db) {
// ...
}
@Override
public void onUpgrade(final sqliteDatabase db,final int oldVersion,final int newVersion) {
// ...
}
private void createTables(final sqliteDatabase db,final String[] statements) {
for (final String sql : statements) {
try {
db.execsql(sql);
} catch (final Exception e) {
L.e("Unable to create table: " + sql,e);
}
}
}
}
好的,现在,问题.
>我的锁是否正确实施?我真的很陌生,我不知道ReentrantLock是不是一个好选择,如果我在适当的时候锁定/解锁
>我的同步方法是否正确实施?我的意思是,我已将synchronized关键字放在我不希望被并发线程中断的方法周围.这是正确的吗?你可以就我的同步使用提出建议吗?
>我该如何重现这个问题?我创建了一个测试,它使用3个线程对数据库进行并发读/写访问,并使用一些Thread.sleep来确保每个线程的db打开/关闭重叠,但它不会崩溃.这真是烦我,我不认为有很多人有这个问题,所以我不知道如何重现.
>我的DbAdapter DbManager技术选择是个好主意吗?有更好的模式吗?
>让DbManager成为单身人士是个好主意吗?