LitePay之查询与实例化的死锁问题

问题描述

实际使用中在同一时间,两个线程分别进行同一个表(litepal中一个表,对应着一个类)的查询与实例化操作,存在着死锁的问题。

下面来自 LitePal GitHub issue中一位网友的描述:

实例化User类的时候发生的事情,先初始化User类的过程中持有User.class锁,发现DataSupport是父类,此时User.class处于being_initialized状态,尝试初始化DataSupport类(想进入being_initialized状态),但是DataSupport.class已经被子线程的查询操作访问先进行初始化(已经进入了being_initialized状态,但查询线程需要等待User的being_initialized状态完成),所以主线程实例化User的时候持有User.class锁,等待DataSupport.class锁;而子线程的查询User操作的时候,先访问的DataSupport类,先初始化DataSupport类,等待访问User.class锁,顺序相反地持锁,死锁BOOM!如果您坚持认为这个是程序逻辑死锁的话,那我们使用LitePal时就不能查询的时候同时在另一个线程实例化该类了。算是库的缺陷么?
类初始化过程持锁的原因 参考链接:https://yq.aliyun.com/articles/73595

代码 & 解决

可能出现死锁的伪代码如下:

class User():LitePalSupport()
// A
Thread(){
    ...
    Log.i("before LitePal#findAll")
    LitePal.findAll()
    Log.i("after LitePal#findAll")
}.start()
// B
Thread(){
    ...
    Log.i("before User instantiation")
    val user = User()
    Log.i("after User instantiation")
}.start()

如上代码, 当线程A执行到 LitePal.findAll() 持有 LitePalSupport.class锁 并等待 User.class的释放 同时 线程B执行到 val user = User() 持有 User.class锁 并等待 LitePalSupport.class 的释放。

由于 User 继承自 LitePalSupport ,所以在User 类初始化时,需要等待 LitePalSupport.class 锁

解决:

class User():LitePalSupport()


val lock = Any()
// A
Thread(){
    ...
    Log.i("before LitePal#findAll")
    Synchronized(lock){ LitePal.findAll() }
    Log.i("after LitePal#findAll")
}.start()
// B
Thread(){
    ...
    Log.i("before User instantiation")
    val user = Synchronized(lock){ User() }
    Log.i("after User instantiation")
}.start()

思路:

  1. 实例化与查询保持在用一个线程中进行
  2. 避免死锁,自己手动增加一个锁。
  • 假笨说-类初始化死锁导致线程被打爆!打爆!爆!
  • Litepal 死锁问题 #101

你可能感兴趣的:(LitePay之查询与实例化的死锁问题)