一、原生 SQLite 的线程安全:需手动保障
Android 底层的 SQLiteDatabase 并非「开箱即用」的线程安全,核心规则如下:
-
SQLite 本身的线程模式 :
SQLite 有 3 种线程模式,Android 系统默认编译为「多线程模式(MULTITHREADED)」:
- 多线程模式:多个线程可以同时读 数据库,但同一时间只能有一个线程写;
- 若违反该规则(比如多线程同时写),会抛出
SQLiteException: database is locked异常。
-
Android 中使用的核心问题:
- 若多个线程共用同一个
SQLiteDatabase实例:系统会通过内置锁保证「单写多读」,但锁竞争可能导致性能下降,甚至极端情况下出现死锁; - 若多个线程创建独立的
SQLiteDatabase实例(对应同一个数据库文件):会触发文件级别的锁冲突,大概率出现「database is locked」异常。
- 若多个线程共用同一个
-
原生 SQLite 保证线程安全的手动方案:
java// 示例:单例 + 同步锁保证 SQLiteDatabase 操作线程安全 public class DBHelper extends SQLiteOpenHelper { private static DBHelper instance; private SQLiteDatabase db; // 单例模式,保证全局只有一个 DBHelper 实例 public static synchronized DBHelper getInstance(Context context) { if (instance == null) { instance = new DBHelper(context.getApplicationContext()); } return instance; } // 所有数据库操作通过该方法执行,加同步锁避免多线程冲突 public synchronized void executeUpdate(String sql) { if (db == null || !db.isOpen()) { db = getWritableDatabase(); } db.execSQL(sql); } }
二、Room 库的线程安全:天然保障(推荐)
Room 是 Google 官方推荐的 ORM 框架,基于 SQLite 封装,设计上从根源规避了线程安全问题:
-
禁止主线程操作 :
Room 默认不允许在主线程执行数据库操作(会抛出
IllegalStateException),必须通过异步方式执行(协程suspend函数、LiveData、Flow、RxJava),从根本上避免主线程阻塞和线程冲突。 -
内置线程安全机制:
- Room 内部通过「数据库连接池」管理连接,自动处理多线程下的连接分配和锁机制;
- 支持
@Transaction注解,保证一组操作的原子性(要么全部成功,要么全部失败),避免多线程下的数据不一致。
-
Room 线程安全的使用示例:
kotlin// 1. DAO 层定义 suspend 函数(协程异步,天然线程安全) @Dao interface UserDao { @Insert suspend fun insertUser(user: User) // suspend 函数只能在协程/其他 suspend 函数中调用 @Transaction // 事务注解,保证操作原子性 @Query("UPDATE user SET name = :name WHERE id = :id") suspend fun updateUserName(id: Int, name: String) } // 2. 调用层(协程中执行,无需手动加锁) viewModelScope.launch(Dispatchers.IO) { userDao.insertUser(User(1, "张三")) userDao.updateUserName(1, "李四") }
三、总结
- 原生
SQLiteDatabase不是开箱即用的线程安全,需通过「单例 + 同步锁」「事务」等手动方式保证,易出错且维护成本高; - Room 作为官方封装库,通过「禁止主线程操作」「内置连接池」「事务支持」天然保证线程安全,是 Android 数据库开发的首选;
- 核心原则:无论使用哪种方式,都要避免多线程直接操作同一个数据库连接,优先通过异步/串行化方式执行数据库操作。