代码之家  ›  专栏  ›  技术社区  ›  mol

关闭sqlite数据库时出现死锁

  •  0
  • mol  · 技术社区  · 7 年前

    我正在调查我项目中的偶然事件。当后台有很多对数据库的请求,用户单击按钮,数据库作为注销的一部分被关闭时,就会发生这种情况。我用的是saferoom和rx-java2。

    这是来自测试项目的一些代码,我试着重现这个问题(重现大约30%的时间):

    class MainActivity : AppCompatActivity() {
    
        lateinit var db: TestDb
        private val disposables = CompositeDisposable()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            openStorage("password")
    
            val d = Observable.interval(100, TimeUnit.MICROSECONDS)
                .map { db.testDao().insert(TestEntity()) }
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ status.text = "WRITING" }, {})
            disposables.add(d)
    
            closeDbButton.setOnClickListener {
                disposables.clear()
                db.close()
                status.text = "DB CLOSED"
            }
        }
    
        @Synchronized
        fun openStorage(storageSecret: String) {
            val factory = SafeHelperFactory(storageSecret.toCharArray())
            db = Room.databaseBuilder(this, TestDb::class.java, TestDb.DATABASE_NAME)
                .openHelperFactory(factory)
                .build()
        }
    }
    

    如您所见,为了模拟这个问题,我试图在循环中访问数据库,同时关闭数据库。即使我在关闭之前释放了写链,死锁还是会发生,应用程序也会冻结。

    我还试图分析线程转储:

    "main" prio=5 tid=1 Waiting
      | group="main" sCount=1 dsCount=0 flags=1 obj=0x727565f0 self=0x7b42cbea00
      | sysTid=24431 nice=-10 cgrp=default sched=0/0 handle=0x7bc78e99a8
      | state=S schedstat=( 3718987857 647207377 6247 ) utm=336 stm=34 core=3 HZ=100
      | stack=0x7fd4c9a000-0x7fd4c9c000 stackSize=8MB
      | held mutexes=
      at java.lang.Object.wait(Native method)
      - waiting on <0x017bb93d> (a java.lang.Object)
      at java.lang.Thread.parkFor$(Thread.java:2137)
      - locked <0x017bb93d> (a java.lang.Object)
      at sun.misc.Unsafe.park(Unsafe.java:358)
      at java.util.concurrent.locks.LockSupport.park(LockSupport.java:190)
      at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:868)
      at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:900)
      at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1223)
      at java.util.concurrent.locks.ReentrantLock$FairSync.lock(ReentrantLock.java:225)
      at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:286)
      at net.sqlcipher.database.SQLiteDatabase.lock(SQLiteDatabase.java:553)
      at net.sqlcipher.database.SQLiteDatabase.close(SQLiteDatabase.java:1284)
      at net.sqlcipher.database.SQLiteOpenHelper.close(SQLiteOpenHelper.java:272)
      - locked <0x064ca532> (a com.commonsware.cwac.saferoom.Helper$1)
      at com.commonsware.cwac.saferoom.Helper$OpenHelper.close(Helper.java:202)
      - locked <0x064ca532> (a com.commonsware.cwac.saferoom.Helper$1)
      at com.commonsware.cwac.saferoom.Helper.close(Helper.java:146)
      at androidx.room.RoomDatabase.close(RoomDatabase.java:189)
      at com.company.deadlocktest.MainActivity$onCreate$1.onClick(MainActivity.kt:38)
      at android.view.View.performClick(View.java:6294)
      at android.view.View$PerformClick.run(View.java:24770)
      at android.os.Handler.handleCallback(Handler.java:790)
      at android.os.Handler.dispatchMessage(Handler.java:99)
      at android.os.Looper.loop(Looper.java:164)
      at android.app.ActivityThread.main(ActivityThread.java:6494)
      at java.lang.reflect.Method.invoke(Native method)
      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
    
    
    "RxComputationThreadPool-1" daemon prio=5 tid=15 Blocked
      | group="main" sCount=1 dsCount=0 flags=1 obj=0x12fc0610 self=0x7b3a839c00
      | sysTid=24450 nice=0 cgrp=default sched=0/0 handle=0x7b2c5384f0
      | state=S schedstat=( 2462204096 250602566 3746 ) utm=209 stm=36 core=1 HZ=100
      | stack=0x7b2c436000-0x7b2c438000 stackSize=1037KB
      | held mutexes=
      at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:-1)
      - waiting to lock <0x064ca532> (a com.commonsware.cwac.saferoom.Helper$1) held by thread 1
      at com.commonsware.cwac.saferoom.Helper$OpenHelper.getWritableSupportDatabase(Helper.java:158)
      at com.commonsware.cwac.saferoom.Helper.getWritableDatabase(Helper.java:120)
      at androidx.room.RoomDatabase.setTransactionSuccessful(RoomDatabase.java:286)
      at com.company.deadlocktest.TestDao_Impl.insert(TestDao_Impl.java:49)
      at com.company.deadlocktest.MainActivity$onCreate$d$1.apply(MainActivity.kt:30)
      at com.company.deadlocktest.MainActivity$onCreate$d$1.apply(MainActivity.kt:18)
      at io.reactivex.internal.operators.observable.ObservableMap$MapObserver.onNext(ObservableMap.java:59)
      at io.reactivex.internal.operators.observable.ObservableInterval$IntervalObserver.run(ObservableInterval.java:83)
      at io.reactivex.internal.schedulers.ScheduledDirectPeriodicTask.run(ScheduledDirectPeriodicTask.java:38)
      at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457)
      at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:307)
      at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:302)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
      at java.lang.Thread.run(Thread.java:764)
    

    如您所见,Rx线程被锁阻塞,由主线程获取( <0x064ca532> com.commonsware.cwac.saferoom.Helper$1 ),主线程处于等待状态。

    所以我试图找出是否有办法避免这个问题,或者这是saferoom/sqlcipher中的某种错误。

    0 回复  |  直到 7 年前
        1
  •  1
  •   CommonsWare    7 年前

    我不是RxJava专家,但我不认为 clear() 会瞬间发生。

    作为解决办法,不要打电话 close() 在主应用程序线程上。通常,我们不会 关闭() 考虑到应用程序中发生的一切,我们根本不知道什么时候是好时机。如果你真的想 关闭() ,然后在 Schedulers.io() 用于数据库访问的线程。

    推荐文章