我有一个包含若干张表的数据库。 我想使用多线程更新这些表。 所有线程都将使用同一实例的SQLiteDatabase。
请问这种做法是否正确? Sqlite数据库是线程安全的吗? 不同的两个线程能否同时更新同一张表但是不同的数据集?
我有一个包含若干张表的数据库。 我想使用多线程更新这些表。 所有线程都将使用同一实例的SQLiteDatabase。
请问这种做法是否正确? Sqlite数据库是线程安全的吗? 不同的两个线程能否同时更新同一张表但是不同的数据集?
默认情况下,它不是线程安全的。你应该使用与锁相关的SQLiteHelper方法来提供线程安全。
[编辑]: SQLiteDatabase类提供了一个默认的锁定机制(请参见注释),如果您在多线程上运行,则无需考虑更改任何内容以实现线程安全。
在此文档中搜索“thread”:http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
并阅读更多:
Android使用Java锁机制来保持SQLite数据库的访问序列化。因此,如果多个线程有一个db实例,它总是以串行方式调用数据库,并且当然数据库是线程安全的。
如果我们确定我们只从单个线程使用数据库,我们可以通过调用setLockingEnable(false)
来禁用数据库内部锁定,但是这个方法已经在API级别16中弃用并不再使用。如果你查看SQLiteDatabase
类中这个方法的实现,你会发现什么也没有写,即空方法。
public void setLockingEnabled (boolean lockingEnabled)
该方法现在不起作用,请勿使用。
我们需要注意的一件事是,应该创建您的助手类的一个实例(即通过使其成为单例)并将同一实例共享到多个线程中,在操作之间不要调用数据库上的close()
,否则您可能会收到以下异常:
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase
因此,在访问数据库时不要调用database.close()
,当所有操作完成时,数据库将自动执行关闭操作。
onDestroy
中调用sqLiteHelper.close
时出现了一些错误。那么我应该怎么做呢?不要完全不调用它吗?当所有操作完成时,_database将在内部自行执行关闭操作。但是,public SQLiteDatabase getWritableDatabase()
说__确保在不再需要数据库时调用close__。 - user25我认为API 16之后这里的答案不再准确。
简而言之:我认为API 16及更高版本不会阻止您在不同线程上同时执行多个SQL语句。
在API 16之前,方法setLockingEnabled
确实存在,javadoc清楚地说明它默认设置为true。一旦该方法在API 16中被弃用并设置为无效,文档中就没有关于锁定是否启用的官方信息了。但是我们可以通过查看代码来获取一些信息:https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/database/sqlite/SQLiteDatabase.java
有一个名为mLock
的字段明确表示它仅用于全局状态更改,而不用于执行SQL语句:
// Shared database state lock.
// This lock guards all of the shared state of the database, such as its
// configuration, whether it is open or closed, and so on. This lock should
// be held for as little time as possible.
//
// The lock MUST NOT be held while attempting to acquire database connections or
// while executing SQL statements on behalf of the client as it can lead to deadlock.
//
// It is ok to hold the lock while reconfiguring the connection pool or dumping
// statistics because those operations are non-reentrant and do not try to acquire
// connections that might be held by other threads.
//
// Basic rule: grab the lock, access or modify global state, release the lock, then
// do the required SQL work.
private final Object mLock = new Object();
此外,所有的SQL工作都是在SQL会话中完成的,每个线程都有自己的会话(下面的引用来自SQLiteSession
):
会话对象不是线程安全的。事实上,会话对象是与线程绑定的。 {@link SQLiteDatabase}使用线程本地变量将会话与每个线程关联,仅供该线程使用。 因此,每个线程都有自己的会话对象,因此其事务状态独立于其他线程。
这与API 15及更早版本不同,在那些版本中,执行语句是直接从数据库中进行的,而不是在会话中进行的:例如https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-4.0.4_r2.1/core/java/android/database/sqlite/SQLiteStatement.java中的executeUpdateDelete
方法实际上会自行获取和释放锁。它调用SQLiteDatabase
上的lock
方法,该方法会执行mLockingEnabled
的检查,然后锁定mLock
对象。通过这种方式,不同线程上不能同时执行两个SQL语句。
SQLiteDatabase
中的synchronized(mLock)
仅围绕全局状态更改,如上面的注释所示 - 并且SQLiteDatabase
上不再有任何lock
方法(由语句等调用)。因此,我找不到任何证据表明Android仍然确保两个不同线程上的SQL语句不能同时执行。如果你做到了...
setLockingEnabled(boolean lockingEnabled) 控制是否通过在关键部分周围使用锁来使SQLiteDatabase线程安全。