日期:2014-05-16  浏览次数:20507 次

SQLiteException:errorcode 5: database is locked

这几天写线程操作,其中涉及到多线程操作数据库的问题。一个读线程不停地在读取数据库数据,另外一个Service不断地采集传感器数据并写入。在手机运行过程中出现上述错误。网络上提及到这个问题不在少数,但是给出的解决方案我基本都看不懂。。

最后找到师兄,介绍了单例模式,大概敲了几行,就解决了。

具体的单例模式还是要深入研究,我就简单把我个人理解的意思写下来吧。

像SQLiteDatabase(或者SQLiteDBHhelper)的实例,只能有一个。也就是操作数据库的链接(Connection)只能有一个,否则就会出现上述错误。

还有很多人提及到ContentProvider,现在的知识水平还并不理解这个东西是做什么用的。

无论是SQLite这种轻量级数据库,还是SQL Server这种大型的数据库,本身都是“线程安全的”,也就是说,像加锁、解锁、防止读脏数据这一类的数据库基本约束都已经由SQLite数据库本身保证,Java或android程序猿是无法左右其是否加解锁的,并且,如果你的程序正确,无论操作频率多高,都是可以运行,宁可崩溃,也不会出现“脏数据”的情况。

那么我的程序中出现了两个线程,由于SQLite数据库自身的安全约束,导致线程读写数据库冲突,某线程无法读写另一线程已经加锁的数据库。

解决的办法就是使用单例模式来严格控制SQLiteDatabase的实例,也就是只能实例化一个SQLiteDatabase对象,无论多少个线程,都只用这一个SQLiteDatabase(或者是DBHelpler)来读写线程。这样共用一个数据库链接就不会产生多个链接同时修改数据的问题了。

以上纯属个人理解。

public class MotionAnalysisDatabase extends SQLiteOpenHelper{

	public MotionAnalysisDatabase(Context context) {
		// 数据库名为MotionAnalysisDatabase,版本号1
		super(context, "MotionAnalysisDatabase", null, 1);
	}
	
	private volatile static MotionAnalysisDatabase uniqueInstance;  
        public static MotionAnalysisDatabase getInstance(Context context) {  
            if (uniqueInstance == null) {  
                synchronized (MotionAnalysisDatabase.class) {  
                    if (uniqueInstance == null) {  
                        uniqueInstance = new MotionAnalysisDatabase(context);  
                    }  
                }  
            }  
            return uniqueInstance;  
        }  
	

	@Override
	public void onCreate(SQLiteDatabase db) {
		
		String createAccTable = "CREATE TABLE acc_table" + " (" + AccTable.ACC_ID
				+ " INTEGER primary key autoincrement, " + AccTable.DATE + " TEXT, " + 
				AccTable.ACCX + " float, "+ AccTable.ACCY + " float, "+AccTable.ACCZ + " float, "+ AccTable.FLAG + " INTEGER);";
		System.out.println("创建运动加速度数据表的语句" + createAccTable);
		db.execSQL(createAccTable);

	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// TODO Auto-generated method stub
		db.execSQL("DROP TABLE IF EXISTS acc_table");

	}

}

在线程中,要是引用MotionAnalysisDatabase,可以这么写:

private MotionAnalysisDatabase mDatabase; private SQLiteDatabase db; 
mDatabase = MotionAnalysisDatabase.getInstance(AccDataCollectorService.this);
db = mDatabase.getWritableDatabase();

这样再引用,就没有在报这个错误了。