多线程访问数据库

在Android中,当多个线程并发访问SQLite数据库时,可能导致数据库被锁定(SQLiteDatabaseLockedException)。每个SQLiteOpenHelper实例都会创建新的数据库连接,所以并发写操作会失败。为了避免这种情况,需要确保同一时间只有一个连接在使用,防止线程间冲突。当线程在使用数据库连接时,其他线程尝试关闭已打开的连接会导致IllegalStateException。因此,正确管理数据库连接和关闭时机至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<span style="font-size:18px;">
</span>

android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

上面的问题,是因为每次创建SQLiteOpenHelper对象时,实际上也是在新建一个数据库连接。如果通过多个连接同时对数据库进行写数据操作,则一定会失败。为确保多线程能安全地操作数据库,则需要保证只有一个数据库连接被占用。

<span style="font-size:18px;">
</span>

java.lang.IllegalStateException:attempt to re-open an already-closed object: SQLiteDatabase

既然只有一个数据库连接,不同的线程会取得一样的SQLiteDatabase对象实例。当线程1尝试管理数据库连接时,线程2却仍然在使用该数据库连接。这就导致了上面问题的原因。因此我们只能在确保数据库没有被占用的情况下,才去关闭它。

DatabaseManager:

<span style="font-size:18px;">public class DatabaseManager {

    private AtomicInteger mOpenCounter = new AtomicInteger();
    private static DatabaseManager mInstance;
    private SQLiteOpenHelper mDatabaseHelper;
    private SQLiteDatabase mDatabase;

    private DatabaseManager(SQLiteOpenHelper helper) {
        mDatabaseHelper = helper;
    }

    public static synchronized void initInstance(SQLiteOpenHelper helper) {
        if (mInstance == null) {
            mInstance = new DatabaseManager(helper);
        }
    }

    public static synchronized DatabaseManager getInstance() {
        if (mInstance == null) {
            throw new IllegalStateException(
                    "DatabaseManager: 请先调用始化操作initInstance");
        }
        return mInstance;
    }

    public void executeQuery(IQueryExecutor executor) {
        SQLiteDatabase database = openDatabase();
        executor.run(database);
        closeDatabase();
    }

    public void executeQueryTask(final IQueryExecutor executor) {
        new Thread(new Runnable() {

            @Override
            public void run() {
                executeQuery(executor);
            }
        }).start();
    }

    private synchronized SQLiteDatabase openDatabase() {
        if (mOpenCounter.incrementAndGet() == 1) {
            mDatabase = mDatabaseHelper.getWritableDatabase();
        }
        return mDatabase;
    }

    private synchronized void closeDatabase() {
        if (mOpenCounter.decrementAndGet() == 0) {
            mDatabase.close();
        }
    }

}</span>

helper文件:

<span style="font-size:18px;">public class AddPathToDBHelper extends SQLiteOpenHelper {

    private static final int START_DATABASE_VERSION = 1;
    private static final int CURRENT_DATABASE_VERSION = 1;
    private static final String DB_NAME = "paths.db";

    public AddPathToDBHelper(Context context) {
        super(context, DB_NAME, null, CURRENT_DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table if not exists " + Table.TABLE_NAME + " ( "
                + Table._ID + " integer primary key autoincrement , "
                + Table.PATH + " text unique not null , " + Table.LAST_MODIFIED
                + " long );");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (oldVersion) {
        case START_DATABASE_VERSION:

            break;

        default:
            break;
        }
    }

}</span>

DAO文件:

<span style="font-size:18px;">public class AddPathDAO {

    interface Table {
        String TABLE_NAME = "allPaths";
        String _ID = "_id";
        String PATH = "path";
        String LAST_MODIFIED = "last_modified";
    }

    private SQLiteDatabase mDatabase;

    public AddPathDAO(SQLiteDatabase database) {
        mDatabase = database;
    }

    public void insertPath(HashMap<String, Long> map) {
        ContentValues values = new ContentValues();
        try {
            mDatabase.beginTransaction();
            for (Entry<String, Long> item : map.entrySet()) {
                values.put(Table.PATH, item.getKey());
                values.put(Table.LAST_MODIFIED, item.getValue());
                mDatabase.insert(Table.TABLE_NAME, null, values);
            }
            mDatabase.setTransactionSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mDatabase.endTransaction();
        }
    }
}</span>

调用用例:

<span style="font-size:18px;">public class MainActivity extends Activity {

    private HashMap<String, Long> map;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DatabaseManager.initInstance(new AddPathToDBHelper(
                getApplicationContext()));
        map = new HashMap<String, Long>();
        map.put("sdcard0/tencet", 1234354566L);
        map.put("sdcard0/tencet1", 1234354566L);
        map.put("sdcard0/tencet2", 1234354566L);
        DatabaseManager.getInstance().executeQuery(new IQueryExecutor() {

            @Override
            public void run(SQLiteDatabase database) {
                AddPathDAO addPathDAO = new AddPathDAO(database);
                addPathDAO.insertPath(map);
            }
        });
    }

}</span>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值