Sqlite 数据库实例线程安全吗
Posted
技术标签:
【中文标题】Sqlite 数据库实例线程安全吗【英文标题】:Is Sqlite Database instance thread safe 【发布时间】:2011-10-04 06:02:12 【问题描述】:我有一个包含一些表的数据库。 我想使用多个线程更新表。 我将在所有线程中使用相同的 SQLiteDatabase 实例。
请建议这种方法是否正确。 Sqlite 数据库线程安全吗? 两个不同的线程能否同时为不同的值集更新同一张表。
【问题讨论】:
What are the best practices for SQLite on android?的可能重复 你应该使用***.com/questions/12758655/…中描述的锁定 【参考方案1】:[错误:] 不,默认情况下它不是线程安全的。您应该使用与锁定相关的 SQLiteHelper 方法来提供线程安全。
[编辑]: SQLiteDatabase 类默认提供锁定机制(请参阅 cmets),如果您在多线程上运行,则不必考虑更改任何内容具有线程安全性。
在本文档中搜索“线程”:http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
阅读更多内容:
http://www.touchtech.co/blog/android-sqlite-locking/ Android threading and database locking Android -- SQLite + SharedPreferences, 2 Threads Simultaneous Read/Write? https://groups.google.com/forum/#!topic/android-developers/s3blUf7CRhU【讨论】:
developer.android.com/reference/android/database/sqlite/…Control whether or not the SQLiteDatabase is made thread-safe by using locks around critical sections. This is pretty expensive, so if you know that your DB will only be used by a single thread then you should set this to false. The default is true
... 这不是意味着它默认是线程安全的吗?
嗯。应该是,现在我错了。嗯。
"此方法在 API 级别 16 中已被弃用。此方法现在什么都不做。请勿使用。"
那么现在当方法被弃用时,SQLiteDatabase 实例在任何情况下都不是线程安全的?
好的,我重读了这个答案——也许你应该把这个答案放到实际状态,而不是 [正确][错误][错误],这很令人困惑。简单回答是不。它不是线程安全的 - 就是这样。 ***.com/questions/12758655/…【参考方案2】:
Android 使用 java 锁定机制来保持 SQLite 数据库访问序列化。所以,如果多个线程有一个数据库实例,它总是以序列化的方式调用数据库,当然数据库是线程安全的。
如果我们确认我们正在使用来自单线程的数据库,我们可以选择通过调用 setLockingEnable(false)
来设置数据库内部锁定禁用,但此方法从 API 级别 16 获得 deprecated 并且不再使用。如果你在SQLiteDatabase
类中看到这个方法的实现,你会发现那里没有写任何东西,即空方法。
public void setLockingEnabled (boolean lockingEnabled)
这个方法现在什么都不做。不要使用。
我们应该注意的一件事是我们应该创建一个辅助类的实例(即通过使其成为单例)并将同一个实例共享给多个线程,并且不要在操作之间调用数据库上的close()
,否则您可能会遇到以下异常:
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase
所以,在访问数据库之间不要调用database.close()
,当所有操作完成后,数据库会在内部自行执行关闭操作。
【讨论】:
是的,我在onDestroy
中调用sqLiteHelper.close
时遇到了一些错误。那么我应该怎么做呢?根本不叫吗? 数据库将在所有操作完成后在内部自行执行关闭操作。 但是public SQLiteDatabase getWritableDatabase()
说确保在不再需要数据库时调用关闭。【参考方案3】:
您可以通过setLockingEnabled 控制您的数据库是否是线程安全的。
通过在关键部分周围使用锁来控制 SQLiteDatabase 是否是线程安全的。这是相当昂贵的,所以如果你知道你的数据库只会被一个线程使用,那么你应该将它设置为 false。默认为真
所以我认为这回答了你的问题。
方法 setLockingEnabled 在 API 级别 16 中已弃用
【讨论】:
【参考方案4】:我认为 API 16 之后这里的答案不再准确。
TL;DR:我认为 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语句了。
相比之下,在现代版本的 Android 中,SQLiteDatabase
中的 synchronized (mLock)
仅围绕全局状态变化,如上述 cmets 所示 - 并且在 SQLiteDatabase
上不再有任何 lock
方法(将通过语句调用等)所以我找不到任何证据表明 Android 仍然可以确保不同线程上的两个 SQL 语句不能同时执行。
【讨论】:
【参考方案5】:如果你成功了..
setLockingEnabled(布尔锁定启用) 通过在关键部分周围使用锁来控制 SQLiteDatabase 是否是线程安全的。
【讨论】:
以上是关于Sqlite 数据库实例线程安全吗的主要内容,如果未能解决你的问题,请参考以下文章