2012-10-06 21 views
10

tôi sử dụng lớp này để quản lý kết nối đến SQLiteDatabase tiềm ẩnSQLiteDatabase multi-thread khóa mẫu

public class BasicDataSource { 

    protected DatabaseHandler dbHelper; 
    protected volatile SQLiteDatabase readable_database; 
    protected volatile SQLiteDatabase writable_database; 
    protected Object read_lock = new Object(); 
    protected Object write_lock = new Object(); 
    protected Context context; 

    protected BasicDataSource(Context ctx) { 
     dbHelper = DatabaseHandler.getInstance(ctx); 
     getReadableDatabase(); 
     dbHelper.onCreate(getWritableDatabase()); 
     this.context = ctx; 
    } 

    public synchronized void close() { 
     dbHelper.close(); 
    } 

    protected void closeInsertHelpers(InsertHelper... helpers) { 
     for (InsertHelper ih : helpers) { 
      if (ih != null) 
       ih.close(); 
     } 
    } 

    protected SQLiteDatabase getReadableDatabase() { 
     synchronized (read_lock) { 
      if (readable_database == null || !readable_database.isOpen()) { 
       readable_database = dbHelper.getReadableDatabase(); 
      } 
      return readable_database; 
     } 
    } 

    protected SQLiteDatabase getWritableDatabase() { 
     synchronized (write_lock) { 
      if (writable_database == null || !writable_database.isOpen()) { 
       writable_database = dbHelper.getWritableDatabase(); 
      } 
      return writable_database; 
     } 
    } 

    protected synchronized void open() throws SQLException { 
     getReadableDatabase(); 
     getWritableDatabase(); 
    } 
} 

Nó chứa hai ổ khóa, một cho đọc, thứ hai cho ghi. Nhưng tôi vẫn thỉnh thoảng nhận được loại trừ:

java.lang.RuntimeException: An error occured while executing doInBackground() 
     at android.os.AsyncTask$3.done(AsyncTask.java:299) 
     at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273) 
     at java.util.concurrent.FutureTask.setException(FutureTask.java:124) 
     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307) 
     at java.util.concurrent.FutureTask.run(FutureTask.java:137) 
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 
     at java.lang.Thread.run(Thread.java:856) 
Caused by: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5): , while compiling: PRAGMA journal_mode 
     at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method) 
     at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:882) 
     at android.database.sqlite.SQLiteConnection.executeForString(SQLiteConnection.java:627) 
     at android.database.sqlite.SQLiteConnection.setJournalMode(SQLiteConnection.java:313) 
     at android.database.sqlite.SQLiteConnection.setWalModeFromConfiguration(SQLiteConnection.java:287) 
     at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:215) 
     at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193) 
     at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463) 
     at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185) 
     at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177) 
     at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:804) 
     at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789) 
     at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694) 
     at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:804) 
     at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221) 
     at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224) 
     at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164) 
     at com.mycompany.myapplication.sql.BasicDataSource.getWritableDatabase(BasicDataSource.java:57) 
     at com.mycompany.myapplication.sql.datasources.SomeDataSource.fillUpDatabaseMethod(SomeDataSource.java:264) 
     at com.mycompany.myapplication.sql.datasources.SomeDataSource.renewCacheMethod(SomeDataSource.java:560) 
     at com.mycompany.myapplication.activities.lists.ListsActivity$Worker.doInBackground(ListsActivity.java:315) 
     at com.mycompany.myapplication.activities.lists.ListsActivity$Worker.doInBackground(ListsActivity.java:1) 
     at android.os.AsyncTask$2.call(AsyncTask.java:287) 
     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) 
     ... 4 more 
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5): , while compiling: PRAGMA journal_mode 
     at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method) 
     at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:882) 
     at android.database.sqlite.SQLiteConnection.executeForString(SQLiteConnection.java:627) 
     at android.database.sqlite.SQLiteConnection.setJournalMode(SQLiteConnection.java:313) 
     at android.database.sqlite.SQLiteConnection.setWalModeFromConfiguration(SQLiteConnection.java:287) 
     at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:215) 
     at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193) 
     at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463) 
     at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185) 
     at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177) 
     at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:804) 
     at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789) 
     at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694) 
     at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:804) 
     at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221) 
     at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224) 
     at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164) 
     at com.mycompany.myapplication.sql.BasicDataSource.getWritableDatabase(BasicDataSource.java:57) 
     at com.mycompany.myapplication.sql.datasources.SomeDataSource.fillUpDatabaseMethod(SomeDataSource.java:264) 
     at com.mycompany.myapplication.sql.datasources.SomeDataSource.renewCacheMethod(SomeDataSource.java:560) 
     at com.mycompany.myapplication.activities.lists.ListsActivity$Worker.doInBackground(ListsActivity.java:315) 
     at com.mycompany.myapplication.activities.lists.ListsActivity$Worker.doInBackground(ListsActivity.java:1) 
     at android.os.AsyncTask$2.call(AsyncTask.java:287) 
     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) 
     at java.util.concurrent.FutureTask.run(FutureTask.java:137) 
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 
     at java.lang.Thread.run(Thread.java:856) 

Có nghĩa là, cơ sở dữ liệu được bằng cách nào đó bị khóa khi cố gắng để thu được khóa trong getWritableDatabase.

SQLiteOpenHelper của tôi là mẫu đơn và Nguồn dữ liệu chỉ sử dụng BasicDataSource làm lớp cha.

Cải tiến tôi có thể làm để tránh SQLiteDatabaseLockedException trong mã được hiển thị là gì?

Trả lời

16

Trong SQLite, có thể có nhiều độc giả tùy ý, nhưng bất kỳ nhà văn nào cũng chặn tất cả người đọc và người viết khác.

Bạn phải sử dụng một khóa duy nhất cho cả người đọc và nhà văn.

Xin lưu ý rằng khóa phải được giữ miễn là bạn đang thực sự truy cập vào cơ sở dữ liệu.


Nếu bạn muốn hỗ trợ nhiều độc giả, sử dụng một khóa mà thực hiện ReadWriteLock, chẳng hạn như ReentrantReadWriteLock. Một cái gì đó như thế này:

class MyData { 
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); 
    private final Lock r = rwl.readLock(); 
    private final Lock w = rwl.writeLock(); 

    public Data ReadSomething(int id) { 
     r.lock(); 
     try { 
      Cursor c = readableDatabase.query(...); 
      return c.getString(0); 
     } finally { 
      r.unlock(); 
     } 
    } 

    public void ChangeSomething(int id, int value) { 
     w.lock(); 
     try { 
      writeableDatabase.update(...); 
     } finally { 
      w.unlock(); 
     } 
    } 
} 
+0

Trong multi-thread niệm, Nếu tôi có nhiều chủ đề sử dụng này/W khóa mẫu R, việc đọc đồng/ghi sẽ không được xử lý, không phải là nó? Ý tôi là, khi hai DataSources đang viết từ hai luồng, khóa này sẽ chỉ hợp lệ cho mỗi DS duy nhất ... –

+0

Mục đích của khóa là phối hợp nhiều luồng. Vì vậy, tất cả các chủ đề sẽ chia sẻ cùng một khóa (trong ví dụ này, lớp 'MyData'). –

+0

Tôi cảm thấy không cần khóa, vì SQLiteDatabase xử lý khóa nội bộ. Ứng dụng đa luồng phức tạp của tôi không cần bất kỳ cơ chế nào như bạn mô tả. Viết khóa được xử lý bởi SQLiteDatabase.beginTransaction/endTransaction. Ngoài ra, tại sao sử dụng lớp Khóa và không đồng bộ hóa từ khóa java? –

Các vấn đề liên quan