【发布时间】:2014-10-10 14:11:10
【问题描述】:
我有一个数据库助手类和三个数据源类,用于同一数据库中的三个表。
通过 AsyncTasks 在很多地方访问数据库。我遇到了这个“尝试重新打开一个已经关闭的对象......”的问题,我四处搜索,发现dbhelper.getReadableDatabase() 为已经打开的连接返回相同的对象。我猜这个问题一定是由于两个线程同时执行操作,其中一个线程完成任务并调用close(),连接被关闭并且正在运行的线程抛出这个异常。
所以为了避免close()我写了以下两种方法:
public static synchronized void newOpenRequest() {
requestsOpen++;
Util.debuglog(TAG, "Open requests: " + requestsOpen);
}
public static synchronized boolean canClose() {
requestsOpen--;
Util.debuglog(TAG, "Open requests: " + requestsOpen);
if(requestsOpen == 0)
return true;
return false;
}
在所有三个数据源类中,当我按以下方式进行操作时:
private void openRead() {
database = dbhelper.getReadableDatabase();
DBHelper.newOpenRequest();
Log.i(TAG, "Database opened.");
}
private void openWrite() {
database = dbhelper.getWritableDatabase();
DBHelper.newOpenRequest();
Log.i(TAG, "Database opened.");
}
private void close() {
if (DBHelper.canClose()) {
dbhelper.close();
Util.debuglog(TAG, "Database closed.");
}
}
我的 LogCat 输出如下:
所以黑色矩形突出显示,openRequests 总数为 0,所以数据库关闭,正常但红色矩形突出显示,
首先 openRequests 是 0,所以只有数据库应该被关闭,但是(我猜)发生的事情是 canClose() 为一个线程返回了 true,就在调用 dbhelper.close(); 另一个线程之前调用 open() (因为 openRequests = 1 就在关闭之前在 LogCat 上),然后调用第一个线程的 close() 给另一个正在运行的线程带来麻烦。
因此寻找解决方案来避免这种并发访问问题。 谢谢。
【问题讨论】:
-
requestsOpen是volatile字段吗? -
@PedroOliveira 不,但我想使用同步方法会在这里产生 volatile 的影响。
-
我不确定。但是由于您可以同时调用 close 和 open(因为它们是相互依赖的同步的),因此不同的任务可以同时更改值。为什么不保留一个在单例中打开的数据库实例并从异步任务中访问它?
-
@PedroOliveira 我将不得不更改很多现有代码,我在某种程度上也尝试过,我保持 DBHelper 单调,将方法打开和关闭从数据源类移到它,但是在调用 dbhelper.close (),它本身递归调用,导致溢出,花了很多时间检查原因,但不能。所以实现了这个逻辑。
-
@PedroOliveira 我正在考虑在一个同步函数中组合以上 3 件事 1)newOpenRequest() 2)canClose() 3)我将在检查 `requestsOpen 后立即关闭同一函数中的数据库连接== 0。你认为这会有所帮助吗?