android sqlite数据库死锁,SQLite也可能出现死锁。

提到锁就不得不说到死锁的问题,而SQLite也可能出现死锁。

下面举个例子:

连接1:BEGIN (UNLOCKED)

连接1:SELECT ... (SHARED)

连接1:INSERT ... (RESERVED)

连接2:BEGIN (UNLOCKED)

连接2:SELECT ... (SHARED)

连接1:COMMIT (PENDING,尝试获取EXCLUSIVE锁,但还有SHARED锁未释放,返回SQLITE_BUSY)

连接2:INSERT ... (尝试获取RESERVED锁,但已有PENDING锁未释放,返回SQLITE_BUSY)

现在2个连接都在等待对方释放锁,于是就死锁了。当然,实际情况并没那么糟糕,任何一方选择不继续等待,回滚事务就行了。

不过要更好地解决这个问题,就必须更深入地了解事务了。

实际上BEGIN语句可以有3种起始状态:

DEFERRED:默认值,开始事务时不获取任何锁。进行第一次读操作时获取SHARED锁,进行第一次写操作时获取RESERVED锁。

IMMEDIATE:开始事务时获取RESERVED锁。

EXCLUSIVE:开始事务时获取EXCLUSIVE锁。

现在考虑2个事务在开始时都使用IMMEDIATE方式:

连接1:BEGIN IMMEDIATE (RESERVED)

连接1:SELECT ... (RESERVED)

连接1:INSERT ... (RESERVED)

连接2:BEGIN IMMEDIATE (尝试获取RESERVED锁,但已有RESERVED锁未释放,因此事务开始失败,返回SQLITE_BUSY,等待用户重试)

连接1:COMMIT (EXCLUSIVE,写入完成后释放)

连接2:BEGIN IMMEDIATE (RESERVED)

连接2:SELECT ... (RESERVED)

连接2:INSERT ... (RESERVED)

连接2:COMMIT (EXCLUSIVE,写入完成后释放)

这样死锁就被避免了。

而EXCLUSIVE方式则更为严苛,即使其他连接以DEFERRED方式开启事务也不会死锁:

连接1:BEGIN EXCLUSIVE (EXCLUSIVE)

连接1:SELECT ... (EXCLUSIVE)

连接1:INSERT ... (EXCLUSIVE)

连接2:BEGIN (UNLOCKED)

连接2:SELECT ... (尝试获取SHARED锁,但已有EXCLUSIVE锁未释放,返回SQLITE_BUSY,等待用户重试)

连接1:COMMIT (EXCLUSIVE,写入完成后释放)

连接2:SELECT ... (SHARED)

连接2:INSERT ... (RESERVED)

连接2:COMMIT (EXCLUSIVE,写入完成后释放)

不过在并发很高的情况下,直接获取EXCLUSIVE锁的难度比较大;而且为了避免EXCLUSIVE状态长期阻塞其他请求,最好的方式还是让所有写事务都以IMMEDIATE方式开始。

顺带一提,要实现重试的话,可以使用sqlite3_busy_timeout()或sqlite3_busy_handler()函数。

由此可见,要想保证线程安全的话,可以有这4种方式:

SQLite使用单线程模式,用一个专门的线程访问数据库。

SQLite使用单线程模式,用一个线程队列来访问数据库,队列一次只允许一个线程执行,队列里的线程共用一个数据库连接。

SQLite使用多线程模式,每个线程创建自己的数据库连接。

SQLite使用串行模式,所有线程共用全局的数据库连接。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值