android 上的 SQLite 数据库、多线程、锁和帐户同步
Posted
技术标签:
【中文标题】android 上的 SQLite 数据库、多线程、锁和帐户同步【英文标题】:SQLite database, multithreading, Locks and account sync on android 【发布时间】:2013-07-17 02:02:09 【问题描述】:我正在尝试获得一种模式,该模式不会因多线程访问我的 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
方法是否正确实施?我的意思是,我已将synchronized
关键字放在我不想被并发线程中断的方法周围。这是正确的吗?你能对我的synchronized
的使用提出建议吗?
如何重现该问题?我创建了一个测试,它使用 3 个线程对数据库进行并发读/写访问,并使用一些 Thread.sleep
来确保每个线程的数据库打开/关闭重叠,但它不会崩溃。这真的很困扰我,我认为有这个问题的人不多,所以我不知道如何重现。
我的DbAdapter
+ DbManager
技术选择是个好主意吗?有更好的模式吗?
将DbManager
设为单例是个好主意吗?
【问题讨论】:
【参考方案1】:对于多线程访问,建议使用单例模式。
这样,对同一个数据库的连续调用将被无缝序列化。
但是,插入一些NullPointerException
s 并非不可能。因此,要扩展您的“Thread.sleep
”逻辑,您可以使用以下代码:
@Override
public SQLiteDatabase getWritableDatabase()
while (true)
try
return super.getWritableDatabase();
catch (SQLiteDatabaseLockedException e)
System.err.println(e);
try
Thread.sleep(500);
catch (InterruptedException e)
System.err.println(e);
【讨论】:
以上是关于android 上的 SQLite 数据库、多线程、锁和帐户同步的主要内容,如果未能解决你的问题,请参考以下文章